Skip to content
Snippets Groups Projects
gimplify.c 257 KiB
Newer Older
  /* Gimplify the function's body.  */
  gimplify_stmt (&DECL_SAVED_TREE (fndecl), &seq);
  outer_bind = gimple_seq_first_stmt (seq);
  if (!outer_bind)
      outer_bind = gimple_build_nop ();
      gimplify_seq_add_stmt (&seq, outer_bind);
  /* The body must contain exactly one statement, a GIMPLE_BIND.  If this is
     not the case, wrap everything in a GIMPLE_BIND to make it so.  */
  if (gimple_code (outer_bind) == GIMPLE_BIND
      && gimple_seq_first (seq) == gimple_seq_last (seq))
    ;
  else
    outer_bind = gimple_build_bind (NULL_TREE, seq, NULL);

  DECL_SAVED_TREE (fndecl) = NULL_TREE;

  /* If we had callee-copies statements, insert them at the beginning
     of the function and clear DECL_VALUE_EXPR_P on the parameters.  */
  if (!gimple_seq_empty_p (parm_stmts))
      gimplify_seq_add_seq (&parm_stmts, gimple_bind_body (outer_bind));
      gimple_bind_set_body (outer_bind, parm_stmts);

      for (parm = DECL_ARGUMENTS (current_function_decl);
	   parm; parm = DECL_CHAIN (parm))
	if (DECL_HAS_VALUE_EXPR_P (parm))
	  {
	    DECL_HAS_VALUE_EXPR_P (parm) = 0;
	    DECL_IGNORED_P (parm) = 0;
	  }
  if (nonlocal_vlas)
    {
      pointer_set_destroy (nonlocal_vlas);
      nonlocal_vlas = NULL;
    }

  pop_gimplify_context (outer_bind);
Diego Novillo's avatar
Diego Novillo committed
  gcc_assert (gimplify_ctxp == NULL);
Joseph Myers's avatar
Joseph Myers committed
  if (!seen_error ())
    verify_gimple_in_seq (gimple_bind_body (outer_bind));

  timevar_pop (TV_TREE_GIMPLIFY);
  input_location = saved_location;
typedef char *char_p; /* For DEF_VEC_P.  */
DEF_VEC_P(char_p);
DEF_VEC_ALLOC_P(char_p,heap);

/* Return whether we should exclude FNDECL from instrumentation.  */

static bool
flag_instrument_functions_exclude_p (tree fndecl)
{
  VEC(char_p,heap) *vec;

  vec = (VEC(char_p,heap) *) flag_instrument_functions_exclude_functions;
  if (VEC_length (char_p, vec) > 0)
    {
      const char *name;
      int i;
      char *s;

      name = lang_hooks.decl_printable_name (fndecl, 0);
      FOR_EACH_VEC_ELT (char_p, vec, i, s)
	if (strstr (name, s) != NULL)
	  return true;
    }

  vec = (VEC(char_p,heap) *) flag_instrument_functions_exclude_files;
  if (VEC_length (char_p, vec) > 0)
    {
      const char *name;
      int i;
      char *s;

      name = DECL_SOURCE_FILE (fndecl);
      FOR_EACH_VEC_ELT (char_p, vec, i, s)
	if (strstr (name, s) != NULL)
	  return true;
    }

  return false;
}

/* Entry point to the gimplification pass.  FNDECL is the FUNCTION_DECL
   node for the function we want to gimplify.
   Return the sequence of GIMPLE statements corresponding to the body

void
gimplify_function_tree (tree fndecl)
{
  gcc_assert (!gimple_body (fndecl));

  oldfn = current_function_decl;
  current_function_decl = fndecl;
  if (DECL_STRUCT_FUNCTION (fndecl))
    push_cfun (DECL_STRUCT_FUNCTION (fndecl));
  else
    push_struct_function (fndecl);
  for (parm = DECL_ARGUMENTS (fndecl); parm ; parm = DECL_CHAIN (parm))
    {
      /* Preliminarily mark non-addressed complex variables as eligible
         for promotion to gimple registers.  We'll transform their uses
         as we find them.  */
      if ((TREE_CODE (TREE_TYPE (parm)) == COMPLEX_TYPE
	   || TREE_CODE (TREE_TYPE (parm)) == VECTOR_TYPE)
          && !TREE_THIS_VOLATILE (parm)
          && !needs_to_live_in_memory (parm))
  if ((TREE_CODE (TREE_TYPE (ret)) == COMPLEX_TYPE
       || TREE_CODE (TREE_TYPE (ret)) == VECTOR_TYPE)
  bind = gimplify_body (fndecl, true);

  /* The tree body of the function is no longer needed, replace it
     with the new GIMPLE body.  */
  seq = gimple_seq_alloc ();
  gimple_seq_add_stmt (&seq, bind);
  gimple_set_body (fndecl, seq);

  /* If we're instrumenting function entry/exit, then prepend the call to
     the entry hook and wrap the whole function in a TRY_FINALLY_EXPR to
     catch the exit hook.  */
  /* ??? Add some way to ignore exceptions for this TFE.  */
  if (flag_instrument_function_entry_exit
      && !DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl)
      && !flag_instrument_functions_exclude_p (fndecl))
      tree x;
      gimple new_bind;
      gimple tf;
      gimple_seq cleanup = NULL, body = NULL;
      x = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS);
      call = gimple_build_call (x, 1, integer_zero_node);
      tmp_var = create_tmp_var (ptr_type_node, "return_addr");
      gimple_call_set_lhs (call, tmp_var);
      gimplify_seq_add_stmt (&cleanup, call);
      x = builtin_decl_implicit (BUILT_IN_PROFILE_FUNC_EXIT);
      call = gimple_build_call (x, 2,
				build_fold_addr_expr (current_function_decl),
				tmp_var);
      gimplify_seq_add_stmt (&cleanup, call);
      tf = gimple_build_try (seq, cleanup, GIMPLE_TRY_FINALLY);
      x = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS);
      call = gimple_build_call (x, 1, integer_zero_node);
      tmp_var = create_tmp_var (ptr_type_node, "return_addr");
      gimple_call_set_lhs (call, tmp_var);
      gimplify_seq_add_stmt (&body, call);
      x = builtin_decl_implicit (BUILT_IN_PROFILE_FUNC_ENTER);
      call = gimple_build_call (x, 2,
				build_fold_addr_expr (current_function_decl),
				tmp_var);
      gimplify_seq_add_stmt (&body, call);
      gimplify_seq_add_stmt (&body, tf);
      new_bind = gimple_build_bind (NULL, body, gimple_bind_block (bind));
      /* Clear the block for BIND, since it is no longer directly inside
         the function, but within a try block.  */
      gimple_bind_set_block (bind, NULL);
      /* Replace the current function body with the body
         wrapped in the try/finally TF.  */
      seq = gimple_seq_alloc ();
      gimple_seq_add_stmt (&seq, new_bind);
      gimple_set_body (fndecl, seq);
  DECL_SAVED_TREE (fndecl) = NULL_TREE;
  cfun->curr_properties = PROP_gimple_any;
  current_function_decl = oldfn;

/* Some transformations like inlining may invalidate the GIMPLE form
   for operands.  This function traverses all the operands in STMT and
   gimplifies anything that is not a valid gimple operand.  Any new
   GIMPLE statements are inserted before *GSI_P.  */

void
gimple_regimplify_operands (gimple stmt, gimple_stmt_iterator *gsi_p)
{
  size_t i, num_ops;
  tree orig_lhs = NULL_TREE, lhs, t;
  gimple_seq pre = NULL;
  gimple post_stmt = NULL;
  struct gimplify_ctx gctx;

  push_gimplify_context (&gctx);
  gimplify_ctxp->into_ssa = gimple_in_ssa_p (cfun);

  switch (gimple_code (stmt))
    {
    case GIMPLE_COND:
      gimplify_expr (gimple_cond_lhs_ptr (stmt), &pre, NULL,
		     is_gimple_val, fb_rvalue);
      gimplify_expr (gimple_cond_rhs_ptr (stmt), &pre, NULL,
		     is_gimple_val, fb_rvalue);
      break;
    case GIMPLE_SWITCH:
      gimplify_expr (gimple_switch_index_ptr (stmt), &pre, NULL,
		     is_gimple_val, fb_rvalue);
      break;
    case GIMPLE_OMP_ATOMIC_LOAD:
      gimplify_expr (gimple_omp_atomic_load_rhs_ptr (stmt), &pre, NULL,
		     is_gimple_val, fb_rvalue);
      break;
    case GIMPLE_ASM:
      {
	size_t i, noutputs = gimple_asm_noutputs (stmt);
	const char *constraint, **oconstraints;
	bool allows_mem, allows_reg, is_inout;

	oconstraints
	  = (const char **) alloca ((noutputs) * sizeof (const char *));
	for (i = 0; i < noutputs; i++)
	  {
	    tree op = gimple_asm_output_op (stmt, i);
	    constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
	    oconstraints[i] = constraint;
	    parse_output_constraint (&constraint, i, 0, 0, &allows_mem,
				     &allows_reg, &is_inout);
	    gimplify_expr (&TREE_VALUE (op), &pre, NULL,
			   is_inout ? is_gimple_min_lval : is_gimple_lvalue,
			   fb_lvalue | fb_mayfail);
	  }
	for (i = 0; i < gimple_asm_ninputs (stmt); i++)
	  {
	    tree op = gimple_asm_input_op (stmt, i);
	    constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
	    parse_input_constraint (&constraint, 0, 0, noutputs, 0,
				    oconstraints, &allows_mem, &allows_reg);
	    if (TREE_ADDRESSABLE (TREE_TYPE (TREE_VALUE (op))) && allows_mem)
	      allows_reg = 0;
	    if (!allows_reg && allows_mem)
	      gimplify_expr (&TREE_VALUE (op), &pre, NULL,
			     is_gimple_lvalue, fb_lvalue | fb_mayfail);
	    else
	      gimplify_expr (&TREE_VALUE (op), &pre, NULL,
			     is_gimple_asm_val, fb_rvalue);
	  }
      }
      break;
    default:
      /* NOTE: We start gimplifying operands from last to first to
	 make sure that side-effects on the RHS of calls, assignments
	 and ASMs are executed before the LHS.  The ordering is not
	 important for other statements.  */
      num_ops = gimple_num_ops (stmt);
      orig_lhs = gimple_get_lhs (stmt);
      for (i = num_ops; i > 0; i--)
	{
	  tree op = gimple_op (stmt, i - 1);
	  if (op == NULL_TREE)
	    continue;
	  if (i == 1 && (is_gimple_call (stmt) || is_gimple_assign (stmt)))
	    gimplify_expr (&op, &pre, NULL, is_gimple_lvalue, fb_lvalue);
	  else if (i == 2
		   && is_gimple_assign (stmt)
		   && num_ops == 2
		   && get_gimple_rhs_class (gimple_expr_code (stmt))
		      == GIMPLE_SINGLE_RHS)
	    gimplify_expr (&op, &pre, NULL,
			   rhs_predicate_for (gimple_assign_lhs (stmt)),
			   fb_rvalue);
	  else if (i == 2 && is_gimple_call (stmt))
	    {
	      if (TREE_CODE (op) == FUNCTION_DECL)
		continue;
	      gimplify_expr (&op, &pre, NULL, is_gimple_call_addr, fb_rvalue);
	    }
	  else
	    gimplify_expr (&op, &pre, NULL, is_gimple_val, fb_rvalue);
	  gimple_set_op (stmt, i - 1, op);
	}

      lhs = gimple_get_lhs (stmt);
      /* If the LHS changed it in a way that requires a simple RHS,
	 create temporary.  */
	{
	  bool need_temp = false;

	  if (is_gimple_assign (stmt)
	      && num_ops == 2
	      && get_gimple_rhs_class (gimple_expr_code (stmt))
		 == GIMPLE_SINGLE_RHS)
	    gimplify_expr (gimple_assign_rhs1_ptr (stmt), &pre, NULL,
			   rhs_predicate_for (gimple_assign_lhs (stmt)),
			   fb_rvalue);
	  else if (is_gimple_reg (lhs))
	    {
	      if (is_gimple_reg_type (TREE_TYPE (lhs)))
		{
		  if (is_gimple_call (stmt))
		    {
		      i = gimple_call_flags (stmt);
		      if ((i & ECF_LOOPING_CONST_OR_PURE)
			  || !(i & (ECF_CONST | ECF_PURE)))
			need_temp = true;
		    }
		  if (stmt_can_throw_internal (stmt))
		    need_temp = true;
		}
	    }
	  else
	    {
	      if (is_gimple_reg_type (TREE_TYPE (lhs)))
		need_temp = true;
	      else if (TYPE_MODE (TREE_TYPE (lhs)) != BLKmode)
		{
		  if (is_gimple_call (stmt))
		    {
		      tree fndecl = gimple_call_fndecl (stmt);

		      if (!aggregate_value_p (TREE_TYPE (lhs), fndecl)
			  && !(fndecl && DECL_RESULT (fndecl)
			       && DECL_BY_REFERENCE (DECL_RESULT (fndecl))))
			need_temp = true;
		    }
		  else
		    need_temp = true;
		}
	    }
	  if (need_temp)
	    {
	      tree temp = create_tmp_reg (TREE_TYPE (lhs), NULL);

	      if (TREE_CODE (orig_lhs) == SSA_NAME)
		orig_lhs = SSA_NAME_VAR (orig_lhs);

	      if (gimple_in_ssa_p (cfun))
		temp = make_ssa_name (temp, NULL);
	      gimple_set_lhs (stmt, temp);
	      post_stmt = gimple_build_assign (lhs, temp);
	      if (TREE_CODE (lhs) == SSA_NAME)
		SSA_NAME_DEF_STMT (lhs) = post_stmt;
	    }
	}
      break;
    }

  if (gimple_referenced_vars (cfun))
    for (t = gimplify_ctxp->temps; t ; t = TREE_CHAIN (t))
      add_referenced_var (t);

  if (!gimple_seq_empty_p (pre))
    {
      if (gimple_in_ssa_p (cfun))
	{
	  gimple_stmt_iterator i;

	  for (i = gsi_start (pre); !gsi_end_p (i); gsi_next (&i))
	    mark_symbols_for_renaming (gsi_stmt (i));
	}
      gsi_insert_seq_before (gsi_p, pre, GSI_SAME_STMT);
    }
  if (post_stmt)
    gsi_insert_after (gsi_p, post_stmt, GSI_NEW_STMT);

  pop_gimplify_context (NULL);
}

/* Expand EXPR to list of gimple statements STMTS.  GIMPLE_TEST_F specifies
   the predicate that will hold for the result.  If VAR is not NULL, make the
   base variable of the final destination be VAR if suitable.  */

tree
force_gimple_operand_1 (tree expr, gimple_seq *stmts,
			gimple_predicate gimple_test_f, tree var)
{
  tree t;
  enum gimplify_status ret;
  struct gimplify_ctx gctx;
  /* gimple_test_f might be more strict than is_gimple_val, make
     sure we pass both.  Just checking gimple_test_f doesn't work
     because most gimple predicates do not work recursively.  */
  if (is_gimple_val (expr)
      && (*gimple_test_f) (expr))
  push_gimplify_context (&gctx);
  gimplify_ctxp->into_ssa = gimple_in_ssa_p (cfun);
Jakub Jelinek's avatar
Jakub Jelinek committed
  gimplify_ctxp->allow_rhs_cond_expr = true;
    expr = build2 (MODIFY_EXPR, TREE_TYPE (var), var, expr);
  if (TREE_CODE (expr) != MODIFY_EXPR
      && TREE_TYPE (expr) == void_type_node)
    {
      gimplify_and_add (expr, stmts);
      expr = NULL_TREE;
    }
  else
    {
      ret = gimplify_expr (&expr, stmts, NULL, gimple_test_f, fb_rvalue);
      gcc_assert (ret != GS_ERROR);
    }
  if (gimple_referenced_vars (cfun))
    for (t = gimplify_ctxp->temps; t ; t = DECL_CHAIN (t))

  pop_gimplify_context (NULL);

  return expr;
}

/* Expand EXPR to list of gimple statements STMTS.  If SIMPLE is true,
   force the result to be either ssa_name or an invariant, otherwise
   just force it to be a rhs expression.  If VAR is not NULL, make the
   base variable of the final destination be VAR if suitable.  */
force_gimple_operand (tree expr, gimple_seq *stmts, bool simple, tree var)
{
  return force_gimple_operand_1 (expr, stmts,
				 simple ? is_gimple_val : is_gimple_reg_rhs,
				 var);
}

/* Invoke force_gimple_operand_1 for EXPR with parameters GIMPLE_TEST_F
   and VAR.  If some statements are produced, emits them at GSI.
   If BEFORE is true.  the statements are appended before GSI, otherwise
   they are appended after it.  M specifies the way GSI moves after
   insertion (GSI_SAME_STMT or GSI_CONTINUE_LINKING are the usual values).  */

tree
force_gimple_operand_gsi_1 (gimple_stmt_iterator *gsi, tree expr,
			    gimple_predicate gimple_test_f,
			    tree var, bool before,
			    enum gsi_iterator_update m)
  expr = force_gimple_operand_1 (expr, &stmts, gimple_test_f, var);
	  for (i = gsi_start (stmts); !gsi_end_p (i); gsi_next (&i))
	    mark_symbols_for_renaming (gsi_stmt (i));
	gsi_insert_seq_before (gsi, stmts, m);
	gsi_insert_seq_after (gsi, stmts, m);
/* Invoke force_gimple_operand_1 for EXPR with parameter VAR.
   If SIMPLE is true, force the result to be either ssa_name or an invariant,
   otherwise just force it to be a rhs expression.  If some statements are
   produced, emits them at GSI.  If BEFORE is true, the statements are
   appended before GSI, otherwise they are appended after it.  M specifies
   the way GSI moves after insertion (GSI_SAME_STMT or GSI_CONTINUE_LINKING
   are the usual values).  */

tree
force_gimple_operand_gsi (gimple_stmt_iterator *gsi, tree expr,
			  bool simple_p, tree var, bool before,
			  enum gsi_iterator_update m)
{
  return force_gimple_operand_gsi_1 (gsi, expr,
				     simple_p
				     ? is_gimple_val : is_gimple_reg_rhs,
				     var, before, m);
}


#include "gt-gimplify.h"