optimize.c   [plain text]


/* Perform optimizations on tree structure.
   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005
   Free Software Foundation, Inc.
   Written by Mark Michell (mark@codesourcery.com).

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "cp-tree.h"
#include "rtl.h"
#include "insn-config.h"
#include "input.h"
#include "integrate.h"
#include "toplev.h"
#include "varray.h"
#include "params.h"
#include "hashtab.h"
#include "target.h"
#include "debug.h"
#include "tree-inline.h"
#include "flags.h"
#include "langhooks.h"
#include "diagnostic.h"
#include "tree-dump.h"
#include "tree-gimple.h"

/* Prototypes.  */

static void update_cloned_parm (tree, tree, bool);
/* APPLE LOCAL begin structor thunks */
static int maybe_alias_body (tree fn, tree clone);
static int maybe_thunk_body (tree fn);
/* APPLE LOCAL end structor thunks */

/* CLONED_PARM is a copy of CLONE, generated for a cloned constructor
   or destructor.  Update it to ensure that the source-position for
   the cloned parameter matches that for the original, and that the
   debugging generation code will be able to find the original PARM.  */

static void
update_cloned_parm (tree parm, tree cloned_parm, bool first)
{
  DECL_ABSTRACT_ORIGIN (cloned_parm) = parm;

  /* We may have taken its address.  */
  TREE_ADDRESSABLE (cloned_parm) = TREE_ADDRESSABLE (parm);

  /* The definition might have different constness.  */
  TREE_READONLY (cloned_parm) = TREE_READONLY (parm);

  TREE_USED (cloned_parm) = !first || TREE_USED (parm);

  /* The name may have changed from the declaration.  */
  DECL_NAME (cloned_parm) = DECL_NAME (parm);
  DECL_SOURCE_LOCATION (cloned_parm) = DECL_SOURCE_LOCATION (parm);
  TREE_TYPE (cloned_parm) = TREE_TYPE (parm);

  DECL_COMPLEX_GIMPLE_REG_P (cloned_parm) = DECL_COMPLEX_GIMPLE_REG_P (parm);
}

/* APPLE LOCAL begin structor thunks */
/* FN is a constructor or destructor, and there are FUNCTION_DECLs cloned from it nearby.
   If the clone and the original funciton have identical parameter lists,
   it is a fully-degenerate (does absolutely nothing) thunk.
   Make the clone an alias for the original function label.  */
static int
maybe_alias_body (tree fn ATTRIBUTE_UNUSED, tree clone ATTRIBUTE_UNUSED)
{
  extern FILE *asm_out_file ATTRIBUTE_UNUSED;

#ifdef ASM_MAYBE_ALIAS_BODY
  ASM_MAYBE_ALIAS_BODY (asm_out_file, fn, clone);
#endif
  return 0;
}

/* FN is a constructor or destructor, and there are FUNCTION_DECLs
   cloned from it nearby.  Instead of cloning this body, leave it
   alone and create tiny one-call bodies for the cloned
   FUNCTION_DECLs.  These clones are sibcall candidates, and their
   resulting code will be very thunk-esque.  */
static int
maybe_thunk_body (tree fn)
{
  tree call, clone, expr_stmt, fn_parm, fn_parm_typelist, last_arg, start;
  int parmno, vtt_parmno;

  /* APPLE LOCAL disable de-cloner */
  if (TRUE || TARGET_KEXTABI || flag_clone_structors)
    return 0;

  /* If we've already seen this structor, avoid re-processing it.  */
  if (TREE_ASM_WRITTEN (fn))
    return 1;

  /* If function accepts variable arguments, give up.  */
  last_arg = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fn)));
  if ( ! VOID_TYPE_P (TREE_VALUE (last_arg)))
       return 0;

  /* If constructor expects vector (AltiVec) arguments, give up.  */
  for (fn_parm = DECL_ARGUMENTS( fn); fn_parm; fn_parm = TREE_CHAIN (fn_parm))
    if (TREE_CODE (fn_parm) == VECTOR_TYPE)
      return 0;

  /* If we don't see a clone, nothing to do.  */
  clone = TREE_CHAIN (fn);
  if (!clone || ! DECL_CLONED_FUNCTION_P (clone))
    return 0;

  /* This is only a win if there are two or more clones.  */
  if ( ! TREE_CHAIN (clone))
    return 0;

  /* Only thunk-ify non-trivial structors.  */
  if (DECL_ESTIMATED_INSNS (fn) < 5)
     return 0;

  /* If we got this far, we've decided to turn the clones into thunks.  */

  /* We're going to generate code for fn, so it is no longer "abstract."  */
  /* APPLE LOCAL begin fix -gused debug info (radar 3271957 3262497) */
  /* Leave 'abstract' bit set for unified constructs and destructors when 
     -gused is used.  */
  if (!(flag_debug_only_used_symbols
	&& DECL_DESTRUCTOR_P (fn)
	&& DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn))
      && !(flag_debug_only_used_symbols
	   && DECL_CONSTRUCTOR_P (fn)
	   && DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn))
      )
    DECL_ABSTRACT (fn) = 0;
  /* APPLE LOCAL end */

  /* Find the vtt_parm, if present.  */
  for (vtt_parmno = -1, parmno = 0, fn_parm = DECL_ARGUMENTS (fn);
       fn_parm;
       ++parmno, fn_parm = TREE_CHAIN (fn_parm))
    {
      if (DECL_ARTIFICIAL (fn_parm) && DECL_NAME (fn_parm) == vtt_parm_identifier)
	{
	  vtt_parmno = parmno;	/* Compensate for removed in_charge parameter.  */
	  break;
	}
    }

  /* We know that any clones immediately follow FN in the TYPE_METHODS
     list.  */
  for (clone = start = TREE_CHAIN (fn);
       clone && DECL_CLONED_FUNCTION_P (clone);
       clone = TREE_CHAIN (clone))
    {
      tree clone_parm, parmlist;

      /* If the clone and original parmlists are identical, turn the clone into an alias.  */
      if (maybe_alias_body (fn, clone))
	continue;

      /* If we've already generated a body for this clone, avoid duplicating it.
	 (Is it possible for a clone-list to grow after we first see it?)  */
      if (DECL_SAVED_TREE (clone) || TREE_ASM_WRITTEN (clone))
	continue;

      /* Start processing the function.  */
      push_to_top_level ();
      start_preparsed_function (clone, NULL_TREE, SF_PRE_PARSED);

      /* Walk parameter lists together, creating parameter list for call to original function.  */
      for (parmno = 0,
	     parmlist = NULL,
	     fn_parm = DECL_ARGUMENTS (fn),
	     fn_parm_typelist = TYPE_ARG_TYPES (TREE_TYPE (fn)),
	     clone_parm = DECL_ARGUMENTS (clone);
	   fn_parm;
	   ++parmno,
	     fn_parm = TREE_CHAIN (fn_parm))
	{
	  if (parmno == vtt_parmno && ! DECL_HAS_VTT_PARM_P (clone))
	    {
	      tree typed_null_pointer_node = copy_node (null_pointer_node);
	      gcc_assert (fn_parm_typelist);
	      /* Clobber actual parameter with formal parameter type.  */
	      TREE_TYPE (typed_null_pointer_node) = TREE_VALUE (fn_parm_typelist);
	      parmlist = tree_cons (NULL, typed_null_pointer_node, parmlist);
	    }
	  else if (parmno == 1 && DECL_HAS_IN_CHARGE_PARM_P (fn))
	    {
	      tree in_charge = copy_node (in_charge_arg_for_name (DECL_NAME (clone)));
	      parmlist = tree_cons (NULL, in_charge, parmlist);
	    }
	  /* Map other parameters to their equivalents in the cloned
	     function.  */
	  else
	    {
	      gcc_assert (clone_parm);
	      DECL_ABSTRACT_ORIGIN (clone_parm) = NULL;
	      parmlist = tree_cons (NULL, clone_parm, parmlist);
	      clone_parm = TREE_CHAIN (clone_parm);
	    }
	  if (fn_parm_typelist)
	    fn_parm_typelist = TREE_CHAIN (fn_parm_typelist);
	}

      /* We built this list backwards; fix now.  */
      parmlist = nreverse (parmlist);
      mark_used (fn);
      call = build_function_call (fn, parmlist);
      expr_stmt = build_stmt (EXPR_STMT, call);
      add_stmt (expr_stmt);

      /* Now, expand this function into RTL, if appropriate.  */
      finish_function (0);
      DECL_ABSTRACT_ORIGIN (clone) = NULL;
      expand_body (clone);
      pop_from_top_level ();
    }
  return 1;
}
/* APPLE LOCAL end structor thunks */

/* FN is a function that has a complete body.  Clone the body as
   necessary.  Returns nonzero if there's no longer any need to
   process the main body.  */

bool
maybe_clone_body (tree fn)
{
  tree clone;
  bool first = true;

  /* We only clone constructors and destructors.  */
  if (!DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
      && !DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (fn))
    return 0;

  /* Emit the DWARF1 abstract instance.  */
  (*debug_hooks->deferred_inline_function) (fn);

  /* We know that any clones immediately follow FN in the TYPE_METHODS
     list.  */
  push_to_top_level ();
  FOR_EACH_CLONE (clone, fn)
    {
      tree parm;
      tree clone_parm;
      /* APPLE LOCAL structor thunks */
      /* Delete some local variables.  */

      /* Update CLONE's source position information to match FN's.  */
      DECL_SOURCE_LOCATION (clone) = DECL_SOURCE_LOCATION (fn);
      DECL_INLINE (clone) = DECL_INLINE (fn);
      DECL_DECLARED_INLINE_P (clone) = DECL_DECLARED_INLINE_P (fn);
      DECL_COMDAT (clone) = DECL_COMDAT (fn);
      DECL_WEAK (clone) = DECL_WEAK (fn);
      DECL_ONE_ONLY (clone) = DECL_ONE_ONLY (fn);
      DECL_SECTION_NAME (clone) = DECL_SECTION_NAME (fn);
      DECL_USE_TEMPLATE (clone) = DECL_USE_TEMPLATE (fn);
      DECL_EXTERNAL (clone) = DECL_EXTERNAL (fn);
      DECL_INTERFACE_KNOWN (clone) = DECL_INTERFACE_KNOWN (fn);
      DECL_NOT_REALLY_EXTERN (clone) = DECL_NOT_REALLY_EXTERN (fn);
      TREE_PUBLIC (clone) = TREE_PUBLIC (fn);
      DECL_VISIBILITY (clone) = DECL_VISIBILITY (fn);
      DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);

      /* Adjust the parameter names and locations.  */
      parm = DECL_ARGUMENTS (fn);
      clone_parm = DECL_ARGUMENTS (clone);
      /* Update the `this' parameter, which is always first.  */
      update_cloned_parm (parm, clone_parm, first);
      parm = TREE_CHAIN (parm);
      clone_parm = TREE_CHAIN (clone_parm);
      if (DECL_HAS_IN_CHARGE_PARM_P (fn))
	parm = TREE_CHAIN (parm);
      if (DECL_HAS_VTT_PARM_P (fn))
	parm = TREE_CHAIN (parm);
      if (DECL_HAS_VTT_PARM_P (clone))
	clone_parm = TREE_CHAIN (clone_parm);
      for (; parm;
	   parm = TREE_CHAIN (parm), clone_parm = TREE_CHAIN (clone_parm))
	/* Update this parameter.  */
	update_cloned_parm (parm, clone_parm, first);
      /* APPLE LOCAL structor thunks */
    }

  /* APPLE LOCAL begin structor thunks */
  /* If we decide to turn clones into thunks, they will branch to fn.
     Must have original function available to call.  */
  if (maybe_thunk_body (fn))
    return 0;
  /* APPLE LOCAL end structor thunks */

  /* APPLE LOCAL begin structor thunks */
  /* We know that any clones immediately follow FN in the TYPE_METHODS
     list.  */
  for (clone = TREE_CHAIN (fn);
       clone && DECL_CLONED_FUNCTION_P (clone);
       clone = TREE_CHAIN (clone))
    {
      tree parm;
      tree clone_parm;
      int parmno;
      splay_tree decl_map;
   /* APPLE LOCAL end structor thunks */

      /* Start processing the function.  */
      start_preparsed_function (clone, NULL_TREE, SF_PRE_PARSED);

      /* Remap the parameters.  */
      decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
      for (parmno = 0,
	     parm = DECL_ARGUMENTS (fn),
	     clone_parm = DECL_ARGUMENTS (clone);
	   parm;
	   ++parmno,
	     parm = TREE_CHAIN (parm))
	{
	  /* Map the in-charge parameter to an appropriate constant.  */
	  if (DECL_HAS_IN_CHARGE_PARM_P (fn) && parmno == 1)
	    {
	      tree in_charge;
	      in_charge = in_charge_arg_for_name (DECL_NAME (clone));
	      splay_tree_insert (decl_map,
				 (splay_tree_key) parm,
				 (splay_tree_value) in_charge);
	    }
	  else if (DECL_ARTIFICIAL (parm)
		   && DECL_NAME (parm) == vtt_parm_identifier)
	    {
	      /* For a subobject constructor or destructor, the next
		 argument is the VTT parameter.  Remap the VTT_PARM
		 from the CLONE to this parameter.  */
	      if (DECL_HAS_VTT_PARM_P (clone))
		{
		  DECL_ABSTRACT_ORIGIN (clone_parm) = parm;
		  splay_tree_insert (decl_map,
				     (splay_tree_key) parm,
				     (splay_tree_value) clone_parm);
		  clone_parm = TREE_CHAIN (clone_parm);
		}
	      /* Otherwise, map the VTT parameter to `NULL'.  */
	      else
		{
		  splay_tree_insert (decl_map,
				     (splay_tree_key) parm,
				     (splay_tree_value) null_pointer_node);
		}
	    }
	  /* Map other parameters to their equivalents in the cloned
	     function.  */
	  else
	    {
	      splay_tree_insert (decl_map,
				 (splay_tree_key) parm,
				 (splay_tree_value) clone_parm);
	      clone_parm = TREE_CHAIN (clone_parm);
	    }
	}

      if (targetm.cxx.cdtor_returns_this ())
	{
	  parm = DECL_RESULT (fn);
	  clone_parm = DECL_RESULT (clone);
	  splay_tree_insert (decl_map, (splay_tree_key) parm,
			     (splay_tree_value) clone_parm);
	}
      /* Clone the body.  */
      clone_body (clone, fn, decl_map);

      /* Clean up.  */
      splay_tree_delete (decl_map);

      /* The clone can throw iff the original function can throw.  */
      cp_function_chain->can_throw = !TREE_NOTHROW (fn);

      /* Now, expand this function into RTL, if appropriate.  */
      finish_function (0);
      BLOCK_ABSTRACT_ORIGIN (DECL_INITIAL (clone)) = DECL_INITIAL (fn);
      expand_or_defer_fn (clone);
      first = false;
    }
  pop_from_top_level ();

  /* We don't need to process the original function any further.  */
  return 1;
}