Skip to content
Snippets Groups Projects
gimplify.cc 549 KiB
Newer Older

static inline void
unmark_visited (tree *tp)
{
  walk_tree (tp, unmark_visited_r, NULL, NULL);
}

/* Likewise, but mark all trees as not visited.  */

static void
unvisit_body (tree fndecl)
Martin Liska's avatar
Martin Liska committed
  struct cgraph_node *cgn = cgraph_node::get (fndecl);
  unmark_visited (&DECL_SAVED_TREE (fndecl));
  unmark_visited (&DECL_SIZE (DECL_RESULT (fndecl)));
  unmark_visited (&DECL_SIZE_UNIT (DECL_RESULT (fndecl)));
    for (cgn = first_nested_function (cgn);
	 cgn; cgn = next_nested_function (cgn))
      unvisit_body (cgn->decl);
/* Unconditionally make an unshared copy of EXPR.  This is used when using
   stored expressions which span multiple functions, such as BINFO_VTABLE,
   as the normal unsharing process can't tell that they're shared.  */

tree
unshare_expr (tree expr)
{
  walk_tree (&expr, mostly_copy_tree_r, NULL, NULL);
  return expr;
}

/* Worker for unshare_expr_without_location.  */

static tree
prune_expr_location (tree *tp, int *walk_subtrees, void *)
{
  if (EXPR_P (*tp))
    SET_EXPR_LOCATION (*tp, UNKNOWN_LOCATION);
  else
    *walk_subtrees = 0;
  return NULL_TREE;
}

/* Similar to unshare_expr but also prune all expression locations
   from EXPR.  */

tree
unshare_expr_without_location (tree expr)
{
  walk_tree (&expr, mostly_copy_tree_r, NULL, NULL);
  if (EXPR_P (expr))
    walk_tree (&expr, prune_expr_location, NULL, NULL);
  return expr;
}

/* Return the EXPR_LOCATION of EXPR, if it (maybe recursively) has
   one, OR_ELSE otherwise.  The location of a STATEMENT_LISTs
   comprising at least one DEBUG_BEGIN_STMT followed by exactly one
   EXPR is the location of the EXPR.  */

static location_t
rexpr_location (tree expr, location_t or_else = UNKNOWN_LOCATION)
{
  if (!expr)
    return or_else;

  if (EXPR_HAS_LOCATION (expr))
    return EXPR_LOCATION (expr);

  if (TREE_CODE (expr) != STATEMENT_LIST)
    return or_else;

  tree_stmt_iterator i = tsi_start (expr);

  bool found = false;
  while (!tsi_end_p (i) && TREE_CODE (tsi_stmt (i)) == DEBUG_BEGIN_STMT)
    {
      found = true;
      tsi_next (&i);
    }

  if (!found || !tsi_one_before_end_p (i))
    return or_else;

  return rexpr_location (tsi_stmt (i), or_else);
}

/* Return TRUE iff EXPR (maybe recursively) has a location; see
   rexpr_location for the potential recursion.  */

static inline bool
rexpr_has_location (tree expr)
{
  return rexpr_location (expr) != UNKNOWN_LOCATION;
}


/* WRAPPER is a code such as BIND_EXPR or CLEANUP_POINT_EXPR which can both
   contain statements and have a value.  Assign its value to a temporary
   and give it void_type_node.  Return the temporary, or NULL_TREE if
   WRAPPER was already void.  */

tree
voidify_wrapper_expr (tree wrapper, tree temp)
  tree type = TREE_TYPE (wrapper);
  if (type && !VOID_TYPE_P (type))
      /* Set p to point to the body of the wrapper.  Loop until we find
	 something that isn't a wrapper.  */
      for (p = &wrapper; p && *p; )
	    case BIND_EXPR:
	      TREE_SIDE_EFFECTS (*p) = 1;
	      TREE_TYPE (*p) = void_type_node;
	      /* For a BIND_EXPR, the body is operand 1.  */
	      p = &BIND_EXPR_BODY (*p);
	      break;

	    case CLEANUP_POINT_EXPR:
	    case TRY_FINALLY_EXPR:
	    case TRY_CATCH_EXPR:
	      TREE_SIDE_EFFECTS (*p) = 1;
	      TREE_TYPE (*p) = void_type_node;
	      p = &TREE_OPERAND (*p, 0);
	      break;

	    case STATEMENT_LIST:
	      {
		tree_stmt_iterator i = tsi_last (*p);
		TREE_SIDE_EFFECTS (*p) = 1;
		TREE_TYPE (*p) = void_type_node;
		p = tsi_end_p (i) ? NULL : tsi_stmt_ptr (i);
	      }
	      break;

	    case COMPOUND_EXPR:
	      /* Advance to the last statement.  Set all container types to
		 void.  */
	      for (; TREE_CODE (*p) == COMPOUND_EXPR; p = &TREE_OPERAND (*p, 1))
		{
		  TREE_SIDE_EFFECTS (*p) = 1;
		  TREE_TYPE (*p) = void_type_node;
		}
	      break;

	    case TRANSACTION_EXPR:
	      TREE_SIDE_EFFECTS (*p) = 1;
	      TREE_TYPE (*p) = void_type_node;
	      p = &TRANSACTION_EXPR_BODY (*p);
	      break;

	      /* Assume that any tree upon which voidify_wrapper_expr is
		 directly called is a wrapper, and that its body is op0.  */
	      if (p == &wrapper)
		{
		  TREE_SIDE_EFFECTS (*p) = 1;
		  TREE_TYPE (*p) = void_type_node;
		  p = &TREE_OPERAND (*p, 0);
		  break;
		}
      if (p == NULL || IS_EMPTY_STMT (*p))
	  /* The wrapper is on the RHS of an assignment that we're pushing
	     down.  */
	  gcc_assert (TREE_CODE (temp) == INIT_EXPR
		      || TREE_CODE (temp) == MODIFY_EXPR);
	  temp = create_tmp_var (type, "retval");
	  *p = build2 (INIT_EXPR, type, temp, *p);
	}

      return temp;
    }

  return NULL_TREE;
}

/* Prepare calls to builtins to SAVE and RESTORE the stack as well as
   a temporary through which they communicate.  */
build_stack_save_restore (gcall **save, gcall **restore)
  *save = gimple_build_call (builtin_decl_implicit (BUILT_IN_STACK_SAVE), 0);
  tmp_var = create_tmp_var (ptr_type_node, "saved_stack");
  gimple_call_set_lhs (*save, tmp_var);
    = gimple_build_call (builtin_decl_implicit (BUILT_IN_STACK_RESTORE),
/* Generate IFN_ASAN_MARK call that poisons shadow of a for DECL variable.  */

static tree
build_asan_poison_call_expr (tree decl)
{
  /* Do not poison variables that have size equal to zero.  */
  tree unit_size = DECL_SIZE_UNIT (decl);
  if (zerop (unit_size))
    return NULL_TREE;

  tree base = build_fold_addr_expr (decl);

  return build_call_expr_internal_loc (UNKNOWN_LOCATION, IFN_ASAN_MARK,
				       void_type_node, 3,
				       build_int_cst (integer_type_node,
				       base, unit_size);
}

/* Generate IFN_ASAN_MARK call that would poison or unpoison, depending
   on POISON flag, shadow memory of a DECL variable.  The call will be
   put on location identified by IT iterator, where BEFORE flag drives
   position where the stmt will be put.  */

static void
asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
		      bool before)
{
  tree unit_size = DECL_SIZE_UNIT (decl);
  tree base = build_fold_addr_expr (decl);

  /* Do not poison variables that have size equal to zero.  */
  if (zerop (unit_size))
    return;

  /* It's necessary to have all stack variables aligned to ASAN granularity
     bytes.  */
  gcc_assert (!hwasan_sanitize_p () || hwasan_sanitize_stack_p ());
  unsigned shadow_granularity
    = hwasan_sanitize_p () ? HWASAN_TAG_GRANULE_SIZE : ASAN_SHADOW_GRANULARITY;
  if (DECL_ALIGN_UNIT (decl) <= shadow_granularity)
    SET_DECL_ALIGN (decl, BITS_PER_UNIT * shadow_granularity);
  HOST_WIDE_INT flags = poison ? ASAN_MARK_POISON : ASAN_MARK_UNPOISON;

  gimple *g
    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
				  build_int_cst (integer_type_node, flags),
				  base, unit_size);

  if (before)
    gsi_insert_before (it, g, GSI_NEW_STMT);
  else
    gsi_insert_after (it, g, GSI_NEW_STMT);
}

/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
   either poisons or unpoisons a DECL.  Created statement is appended
   to SEQ_P gimple sequence.  */

static void
asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
{
  gimple_stmt_iterator it = gsi_last (*seq_p);
  bool before = false;

  if (gsi_end_p (it))
    before = true;

  asan_poison_variable (decl, poison, &it, before);
}

/* Sort pair of VAR_DECLs A and B by DECL_UID.  */

static int
sort_by_decl_uid (const void *a, const void *b)
{
  const tree *t1 = (const tree *)a;
  const tree *t2 = (const tree *)b;

  int uid1 = DECL_UID (*t1);
  int uid2 = DECL_UID (*t2);

  if (uid1 < uid2)
    return -1;
  else if (uid1 > uid2)
    return 1;
  else
    return 0;
}

/* Generate IFN_ASAN_MARK internal call for all VARIABLES
   depending on POISON flag.  Created statement is appended
   to SEQ_P gimple sequence.  */

static void
asan_poison_variables (hash_set<tree> *variables, bool poison, gimple_seq *seq_p)
{
  unsigned c = variables->elements ();
  if (c == 0)
    return;

  auto_vec<tree> sorted_variables (c);

  for (hash_set<tree>::iterator it = variables->begin ();
       it != variables->end (); ++it)
    sorted_variables.safe_push (*it);

  sorted_variables.qsort (sort_by_decl_uid);

  unsigned i;
  tree var;
  FOR_EACH_VEC_ELT (sorted_variables, i, var)
    {
      asan_poison_variable (var, poison, seq_p);

      /* Add use_after_scope_memory attribute for the variable in order
	 to prevent re-written into SSA.  */
      if (!lookup_attribute (ASAN_USE_AFTER_SCOPE_ATTRIBUTE,
			     DECL_ATTRIBUTES (var)))
	DECL_ATTRIBUTES (var)
	  = tree_cons (get_identifier (ASAN_USE_AFTER_SCOPE_ATTRIBUTE),
		       integer_one_node,
		       DECL_ATTRIBUTES (var));
    }
/* Gimplify a BIND_EXPR.  Just voidify and recurse.  */

static enum gimplify_status
gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
{
  tree bind_expr = *expr_p;
  bool old_save_stack = gimplify_ctxp->save_stack;
  tree t;
  location_t start_locus = 0, end_locus = 0;
  tree temp = voidify_wrapper_expr (bind_expr, NULL);
  /* Mark variables seen in this bind expr.  */
  for (t = BIND_EXPR_VARS (bind_expr); t ; t = DECL_CHAIN (t))
	{
	  struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp;

	  /* Mark variable as local.  */
	  if (ctx && ctx->region_type != ORT_NONE && !DECL_EXTERNAL (t))
	      if (! DECL_SEEN_IN_BIND_EXPR_P (t)
		  || splay_tree_lookup (ctx->variables,
					(splay_tree_key) t) == NULL)
		{
		  if (ctx->region_type == ORT_SIMD
		      && TREE_ADDRESSABLE (t)
		      && !TREE_STATIC (t))
		    {
		      if (TREE_CODE (DECL_SIZE_UNIT (t)) != INTEGER_CST)
			ctx->add_safelen1 = true;
		      else
			flag = GOVD_PRIVATE;
		    }
		  omp_add_variable (ctx, t, flag | GOVD_SEEN);
		}
	      /* Static locals inside of target construct or offloaded
		 routines need to be "omp declare target".  */
	      if (TREE_STATIC (t))
		for (; ctx; ctx = ctx->outer_context)
		  if ((ctx->region_type & ORT_TARGET) != 0)
		    {
		      if (!lookup_attribute ("omp declare target",
					     DECL_ATTRIBUTES (t)))
			{
			  tree id = get_identifier ("omp declare target");
			  DECL_ATTRIBUTES (t)
			    = tree_cons (id, NULL_TREE, DECL_ATTRIBUTES (t));
			  varpool_node *node = varpool_node::get (t);
			  if (node)
			    {
			      node->offloadable = 1;
			      if (ENABLE_OFFLOADING && !DECL_EXTERNAL (t))
				{
				  g->have_offload = true;
				  if (!in_lto_p)
				    vec_safe_push (offload_vars, t);
				}
			    }
			}
		      break;
		    }

	  DECL_SEEN_IN_BIND_EXPR_P (t) = 1;

	  if (DECL_HARD_REGISTER (t) && !is_global_var (t) && cfun)
	    cfun->has_local_explicit_reg_vars = true;
  bind_stmt = gimple_build_bind (BIND_EXPR_VARS (bind_expr), NULL,
  gimple_push_bind_expr (bind_stmt);
  gimplify_ctxp->save_stack = false;

  /* Gimplify the body into the GIMPLE_BIND tuple's body.  */
  body = NULL;
  gimplify_stmt (&BIND_EXPR_BODY (bind_expr), &body);
  gimple_bind_set_body (bind_stmt, body);
  /* Source location wise, the cleanup code (stack_restore and clobbers)
     belongs to the end of the block, so propagate what we have.  The
     stack_save operation belongs to the beginning of block, which we can
     infer from the bind_expr directly if the block has no explicit
     assignment.  */
  if (BIND_EXPR_BLOCK (bind_expr))
    {
      end_locus = BLOCK_SOURCE_END_LOCATION (BIND_EXPR_BLOCK (bind_expr));
      start_locus = BLOCK_SOURCE_LOCATION (BIND_EXPR_BLOCK (bind_expr));
    }
  if (start_locus == 0)
    start_locus = EXPR_LOCATION (bind_expr);


  /* If the code both contains VLAs and calls alloca, then we cannot reclaim
     the stack space allocated to the VLAs.  */
  if (gimplify_ctxp->save_stack && !gimplify_ctxp->keep_stack)
      gcall *stack_restore;

      /* Save stack on entry and restore it on exit.  Add a try_finally
	 block to achieve this.  */
      build_stack_save_restore (&stack_save, &stack_restore);

      gimple_set_location (stack_save, start_locus);
      gimple_set_location (stack_restore, end_locus);

      gimplify_seq_add_stmt (&cleanup, stack_restore);
    }

  /* Add clobbers for all variables that go out of scope.  */
  for (t = BIND_EXPR_VARS (bind_expr); t ; t = DECL_CHAIN (t))
    {
	  && DECL_CONTEXT (t) == current_function_decl)
	  if (!DECL_HARD_REGISTER (t)
	      && !TREE_THIS_VOLATILE (t)
	      && !DECL_HAS_VALUE_EXPR_P (t)
	      /* Only care for variables that have to be in memory.  Others
		 will be rewritten into SSA names, hence moved to the
		 top-level.  */
	      && !is_gimple_reg (t)
	      && flag_stack_reuse != SR_NONE)
	    {
	      tree clobber = build_clobber (TREE_TYPE (t), CLOBBER_EOL);
	      gimple *clobber_stmt;
	      clobber_stmt = gimple_build_assign (t, clobber);
	      gimple_set_location (clobber_stmt, end_locus);
	      gimplify_seq_add_stmt (&cleanup, clobber_stmt);
	    }

	  if (flag_openacc && oacc_declare_returns != NULL)
	    {
	      tree key = t;
	      if (DECL_HAS_VALUE_EXPR_P (key))
		{
		  key = DECL_VALUE_EXPR (key);
		  if (TREE_CODE (key) == INDIRECT_REF)
		    key = TREE_OPERAND (key, 0);
		}
	      tree *c = oacc_declare_returns->get (key);
	      if (c != NULL)
		{
		  if (ret_clauses)
		    OMP_CLAUSE_CHAIN (*c) = ret_clauses;

		  ret_clauses = unshare_expr (*c);
		  oacc_declare_returns->remove (key);
		  if (oacc_declare_returns->is_empty ())
		    {
		      delete oacc_declare_returns;
		      oacc_declare_returns = NULL;
		    }
		}
	    }

      if (asan_poisoned_variables != NULL
	  && asan_poisoned_variables->contains (t))
	{
	  asan_poisoned_variables->remove (t);
	  asan_poison_variable (t, true, &cleanup);
	}

      if (gimplify_ctxp->live_switch_vars != NULL
	  && gimplify_ctxp->live_switch_vars->contains (t))
	gimplify_ctxp->live_switch_vars->remove (t);
  if (ret_clauses)
    {
      gomp_target *stmt;
      gimple_stmt_iterator si = gsi_start (cleanup);

      stmt = gimple_build_omp_target (NULL, GF_OMP_TARGET_KIND_OACC_DECLARE,
				      ret_clauses);
      gsi_insert_seq_before_without_update (&si, stmt, GSI_NEW_STMT);
    }

      gs = gimple_build_try (gimple_bind_body (bind_stmt), cleanup,
      if (stack_save)
	gimplify_seq_add_stmt (&new_body, stack_save);
      gimplify_seq_add_stmt (&new_body, gs);
      gimple_bind_set_body (bind_stmt, new_body);
  /* keep_stack propagates all the way up to the outermost BIND_EXPR.  */
  if (!gimplify_ctxp->keep_stack)
    gimplify_ctxp->keep_stack = old_keep_stack;
  gimplify_ctxp->save_stack = old_save_stack;
  gimple_pop_bind_expr ();

  gimplify_seq_add_stmt (pre_p, bind_stmt);
  if (temp)
    {
      *expr_p = temp;
      return GS_OK;
    }
/* Maybe add early return predict statement to PRE_P sequence.  */

static void
maybe_add_early_return_predict_stmt (gimple_seq *pre_p)
{
  /* If we are not in a conditional context, add PREDICT statement.  */
  if (gimple_conditional_context ())
    {
      gimple *predict = gimple_build_predict (PRED_TREE_EARLY_RETURN,
					      NOT_TAKEN);
      gimplify_seq_add_stmt (pre_p, predict);
    }
}

/* Gimplify a RETURN_EXPR.  If the expression to be returned is not a
   GIMPLE value, it is assigned to a new temporary and the statement is
   re-written to return the temporary.

   PRE_P points to the sequence where side effects that must happen before
   STMT should be stored.  */

static enum gimplify_status
gimplify_return_expr (tree stmt, gimple_seq *pre_p)
  tree ret_expr = TREE_OPERAND (stmt, 0);
  if (ret_expr == error_mark_node)
    return GS_ERROR;

  if (!ret_expr
      || TREE_CODE (ret_expr) == RESULT_DECL)
      maybe_add_early_return_predict_stmt (pre_p);
      greturn *ret = gimple_build_return (ret_expr);
      copy_warning (ret, stmt);
      gimplify_seq_add_stmt (pre_p, ret);
      return GS_ALL_DONE;
    }

  if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
  else if (TREE_CODE (ret_expr) == COMPOUND_EXPR)
    {
      /* Used in C++ for handling EH cleanup of the return value if a local
	 cleanup throws.  Assume the front-end knows what it's doing.  */
      result_decl = DECL_RESULT (current_function_decl);
      /* But crash if we end up trying to modify ret_expr below.  */
      ret_expr = NULL_TREE;
    }
      result_decl = TREE_OPERAND (ret_expr, 0);

      /* See through a return by reference.  */
      if (TREE_CODE (result_decl) == INDIRECT_REF)
	result_decl = TREE_OPERAND (result_decl, 0);

      gcc_assert ((TREE_CODE (ret_expr) == MODIFY_EXPR
		   || TREE_CODE (ret_expr) == INIT_EXPR)
		  && TREE_CODE (result_decl) == RESULT_DECL);
  /* If aggregate_value_p is true, then we can return the bare RESULT_DECL.
     Recall that aggregate_value_p is FALSE for any aggregate type that is
     returned in registers.  If we're returning values in registers, then
     we don't want to extend the lifetime of the RESULT_DECL, particularly
     across another call.  In addition, for those aggregates for which
     hard_function_value generates a PARALLEL, we'll die during normal
     expansion of structure assignments; there's special code in expand_return
     to handle this case that does not exist in expand_expr.  */
  if (!result_decl)
    result = NULL_TREE;
  else if (aggregate_value_p (result_decl, TREE_TYPE (current_function_decl)))
    {
      if (!poly_int_tree_p (DECL_SIZE (result_decl)))
	{
	  if (!TYPE_SIZES_GIMPLIFIED (TREE_TYPE (result_decl)))
	    gimplify_type_sizes (TREE_TYPE (result_decl), pre_p);
	  /* Note that we don't use gimplify_vla_decl because the RESULT_DECL
	     should be effectively allocated by the caller, i.e. all calls to
	     this function must be subject to the Return Slot Optimization.  */
	  gimplify_one_sizepos (&DECL_SIZE (result_decl), pre_p);
	  gimplify_one_sizepos (&DECL_SIZE_UNIT (result_decl), pre_p);
	}
      result = result_decl;
    }
  else if (gimplify_ctxp->return_temp)
    result = gimplify_ctxp->return_temp;
  else
    {
      result = create_tmp_reg (TREE_TYPE (result_decl));

      /* ??? With complex control flow (usually involving abnormal edges),
	 we can wind up warning about an uninitialized value for this.  Due
	 to how this variable is constructed and initialized, this is never
	 true.  Give up and never warn.  */
      suppress_warning (result, OPT_Wuninitialized);
  /* Smash the lhs of the MODIFY_EXPR to the temporary we plan to use.
     Then gimplify the whole thing.  */
  if (result != result_decl)
    TREE_OPERAND (ret_expr, 0) = result;

  gimplify_and_add (TREE_OPERAND (stmt, 0), pre_p);
  maybe_add_early_return_predict_stmt (pre_p);
  ret = gimple_build_return (result);
  copy_warning (ret, stmt);
  gimplify_seq_add_stmt (pre_p, ret);
/* Gimplify a variable-length array DECL.  */

gimplify_vla_decl (tree decl, gimple_seq *seq_p)
{
  /* This is a variable-sized decl.  Simplify its size and mark it
     for deferred expansion.  */
  gimplify_one_sizepos (&DECL_SIZE (decl), seq_p);
  gimplify_one_sizepos (&DECL_SIZE_UNIT (decl), seq_p);
Jason Merrill's avatar
Jason Merrill committed
  /* Don't mess with a DECL_VALUE_EXPR set by the front-end.  */
  if (DECL_HAS_VALUE_EXPR_P (decl))
    return;

  /* All occurrences of this decl in final gimplified code will be
     replaced by indirection.  Setting DECL_VALUE_EXPR does two
     things: First, it lets the rest of the gimplifier know what
     replacement to use.  Second, it lets the debug info know
     where to find the value.  */
  ptr_type = build_pointer_type (TREE_TYPE (decl));
  addr = create_tmp_var (ptr_type, get_name (decl));
  DECL_IGNORED_P (addr) = 0;
  t = build_fold_indirect_ref (addr);
  SET_DECL_VALUE_EXPR (decl, t);
  DECL_HAS_VALUE_EXPR_P (decl) = 1;

  t = build_alloca_call_expr (DECL_SIZE_UNIT (decl), DECL_ALIGN (decl),
			      max_int_size_in_bytes (TREE_TYPE (decl)));
Eric Botcazou's avatar
Eric Botcazou committed
  /* The call has been built for a variable-sized object.  */
  CALL_ALLOCA_FOR_VAR_P (t) = 1;
  t = build2 (MODIFY_EXPR, TREE_TYPE (addr), addr, t);

  /* Record the dynamic allocation associated with DECL if requested.  */
  if (flag_callgraph_info & CALLGRAPH_INFO_DYNAMIC_ALLOC)
    record_dynamic_alloc (decl);
/* A helper function to be called via walk_tree.  Mark all labels under *TP
   as being forced.  To be called for DECL_INITIAL of static variables.  */

static tree
force_labels_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
{
  if (TYPE_P (*tp))
    *walk_subtrees = 0;
  if (TREE_CODE (*tp) == LABEL_DECL)
    {
      FORCED_LABEL (*tp) = 1;
      cfun->has_forced_label_in_static = 1;
    }
/* Generate an initialization to automatic variable DECL based on INIT_TYPE.
   Build a call to internal const function DEFERRED_INIT:
   1st argument: SIZE of the DECL;
   2nd argument: INIT_TYPE;
   3rd argument: NAME of the DECL;

   as LHS = DEFERRED_INIT (SIZE of the DECL, INIT_TYPE, NAME of the DECL).  */

static void
gimple_add_init_for_auto_var (tree decl,
			      enum auto_init_type init_type,
			      gimple_seq *seq_p)
{
  gcc_assert (auto_var_p (decl));
  gcc_assert (init_type > AUTO_INIT_UNINITIALIZED);
  location_t loc = EXPR_LOCATION (decl);
  tree decl_size = TYPE_SIZE_UNIT (TREE_TYPE (decl));

  tree init_type_node
    = build_int_cst (integer_type_node, (int) init_type);
    decl_name = build_string_literal (DECL_NAME (decl));

  else
    {
      char *decl_name_anonymous = xasprintf ("D.%u", DECL_UID (decl));
      decl_name = build_string_literal (decl_name_anonymous);

  tree call = build_call_expr_internal_loc (loc, IFN_DEFERRED_INIT,
		 			    TREE_TYPE (decl), 3,
					    decl_size, init_type_node,

  gimplify_assign (decl, call, seq_p);
}

/* Generate padding initialization for automatic vairable DECL.
   C guarantees that brace-init with fewer initializers than members
   aggregate will initialize the rest of the aggregate as-if it were
   static initialization.  In turn static initialization guarantees
   that padding is initialized to zero. So, we always initialize paddings
   to zeroes regardless INIT_TYPE.
   To do the padding initialization, we insert a call to
   __builtin_clear_padding (&decl, 0, for_auto_init = true).
   Note, we add an additional dummy argument for __builtin_clear_padding,
   'for_auto_init' to distinguish whether this call is for automatic
   variable initialization or not.
   */
static void
gimple_add_padding_init_for_auto_var (tree decl, bool is_vla,
				      gimple_seq *seq_p)
{
  tree addr_of_decl = NULL_TREE;
  tree fn = builtin_decl_explicit (BUILT_IN_CLEAR_PADDING);

  if (is_vla)
    {
      /* The temporary address variable for this vla should be
	 created in gimplify_vla_decl.  */
      gcc_assert (DECL_HAS_VALUE_EXPR_P (decl));
      gcc_assert (TREE_CODE (DECL_VALUE_EXPR (decl)) == INDIRECT_REF);
      addr_of_decl = TREE_OPERAND (DECL_VALUE_EXPR (decl), 0);
    }
  else
    {
      mark_addressable (decl);
      addr_of_decl = build_fold_addr_expr (decl);
    }

  gimple *call = gimple_build_call (fn, 2, addr_of_decl,
				    build_one_cst (TREE_TYPE (addr_of_decl)));
  gimplify_seq_add_stmt (seq_p, call);
}

/* Return true if the DECL need to be automaticly initialized by the
   compiler.  */
static bool
is_var_need_auto_init (tree decl)
{
  if (auto_var_p (decl)
Richard Biener's avatar
Richard Biener committed
      && (TREE_CODE (decl) != VAR_DECL
	  || !DECL_HARD_REGISTER (decl))
      && (flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
      && (!lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl)))
      && !OPAQUE_TYPE_P (TREE_TYPE (decl))
      && !is_empty_type (TREE_TYPE (decl)))
/* Gimplify a DECL_EXPR node *STMT_P by making any necessary allocation
   and initialization explicit.  */

static enum gimplify_status
gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
{
  tree stmt = *stmt_p;
  tree decl = DECL_EXPR_DECL (stmt);

  *stmt_p = NULL_TREE;

  if (TREE_TYPE (decl) == error_mark_node)
    return GS_ERROR;

      && !TYPE_SIZES_GIMPLIFIED (TREE_TYPE (decl)))
    {
      gimplify_type_sizes (TREE_TYPE (decl), seq_p);
      if (TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE)
	gimplify_type_sizes (TREE_TYPE (TREE_TYPE (decl)), seq_p);
    }
  /* ??? DECL_ORIGINAL_TYPE is streamed for LTO so it needs to be gimplified
     in case its size expressions contain problematic nodes like CALL_EXPR.  */
  if (TREE_CODE (decl) == TYPE_DECL
      && DECL_ORIGINAL_TYPE (decl)
      && !TYPE_SIZES_GIMPLIFIED (DECL_ORIGINAL_TYPE (decl)))
    {
      gimplify_type_sizes (DECL_ORIGINAL_TYPE (decl), seq_p);
      if (TREE_CODE (DECL_ORIGINAL_TYPE (decl)) == REFERENCE_TYPE)
	gimplify_type_sizes (TREE_TYPE (DECL_ORIGINAL_TYPE (decl)), seq_p);
    }
    {
      tree init = DECL_INITIAL (decl);
      bool is_vla = false;
      /* Check whether a decl has FE created VALUE_EXPR here BEFORE
	 gimplify_vla_decl creates VALUE_EXPR for a vla decl.
	 If the decl has VALUE_EXPR that was created by FE (usually
	 C++FE), it's a proxy varaible, and FE already initialized
	 the VALUE_EXPR of it, we should not initialize it anymore.  */
      bool decl_had_value_expr_p = DECL_HAS_VALUE_EXPR_P (decl);
      poly_uint64 size;
      if (!poly_int_tree_p (DECL_SIZE_UNIT (decl), &size)
	  || (!TREE_STATIC (decl)
	      && flag_stack_check == GENERIC_STACK_CHECK
	      && maybe_gt (size,
			   (unsigned HOST_WIDE_INT) STACK_CHECK_MAX_VAR_SIZE)))
	{
	  gimplify_vla_decl (decl, seq_p);
	  is_vla = true;
	}

	  && !is_vla
	  && TREE_ADDRESSABLE (decl)
Martin Liska's avatar
Martin Liska committed
	  && !TREE_STATIC (decl)
	  && !DECL_HAS_VALUE_EXPR_P (decl)
	  && DECL_ALIGN (decl) <= MAX_SUPPORTED_STACK_ALIGNMENT
	  && !gimplify_omp_ctxp
	  /* GNAT introduces temporaries to hold return values of calls in
	     initializers of variables defined in other units, so the
	     declaration of the variable is discarded completely.  We do not
	     want to issue poison calls for such dropped variables.  */
	  && (DECL_SEEN_IN_BIND_EXPR_P (decl)
	      || (DECL_ARTIFICIAL (decl) && DECL_NAME (decl) == NULL_TREE)))
	{
	  asan_poisoned_variables->add (decl);
	  asan_poison_variable (decl, false, seq_p);
Martin Liska's avatar
Martin Liska committed
	  if (!DECL_ARTIFICIAL (decl) && gimplify_ctxp->live_switch_vars)
	    gimplify_ctxp->live_switch_vars->add (decl);
	}
      /* Some front ends do not explicitly declare all anonymous
	 artificial variables.  We compensate here by declaring the
	 variables, though it would be better if the front ends would
	 explicitly declare them.  */
      if (!DECL_SEEN_IN_BIND_EXPR_P (decl)
	  && DECL_ARTIFICIAL (decl) && DECL_NAME (decl) == NULL_TREE)
	gimple_add_tmp_var (decl);

      if (init && init != error_mark_node)
	{
	  if (!TREE_STATIC (decl))
	    {
	      DECL_INITIAL (decl) = NULL_TREE;
	      init = build2 (INIT_EXPR, void_type_node, decl, init);
	      gimplify_and_add (init, seq_p);
	      ggc_free (init);
	      /* Clear TREE_READONLY if we really have an initialization.  */
	      if (!DECL_INITIAL (decl)
		  && !omp_privatize_by_reference (decl))
		TREE_READONLY (decl) = 0;
	    }
	  else
	    /* We must still examine initializers for static variables
	       as they may contain a label address.  */
	    walk_tree (&init, force_labels_r, NULL, NULL);
	}
      /* When there is no explicit initializer, if the user requested,
	 We should insert an artifical initializer for this automatic
	 variable.  */
      else if (is_var_need_auto_init (decl)
	       && !decl_had_value_expr_p)
	{
	  gimple_add_init_for_auto_var (decl,
					flag_auto_var_init,
					seq_p);
	  /* The expanding of a call to the above .DEFERRED_INIT will apply
	     block initialization to the whole space covered by this variable.
	     As a result, all the paddings will be initialized to zeroes
	     for zero initialization and 0xFE byte-repeatable patterns for
	     pattern initialization.
	     In order to make the paddings as zeroes for pattern init, We
	     should add a call to __builtin_clear_padding to clear the
	     paddings to zero in compatiple with CLANG.
	     We cannot insert this call if the variable is a gimple register
	     since __builtin_clear_padding will take the address of the
	     variable.  As a result, if a long double/_Complex long double
	     variable will spilled into stack later, its padding is 0XFE.  */
	  if (flag_auto_var_init == AUTO_INIT_PATTERN
	      && !is_gimple_reg (decl)
	      && clear_padding_type_may_have_padding_p (TREE_TYPE (decl)))
	    gimple_add_padding_init_for_auto_var (decl, is_vla, seq_p);
	}
/* Gimplify a LOOP_EXPR.  Normally this just involves gimplifying the body
   and replacing the LOOP_EXPR with goto, but if the loop contains an
   EXIT_EXPR, we need to append a label for it to jump to.  */

static enum gimplify_status
gimplify_loop_expr (tree *expr_p, gimple_seq *pre_p)
{
  tree saved_label = gimplify_ctxp->exit_label;
  tree start_label = create_artificial_label (UNKNOWN_LOCATION);
  gimplify_seq_add_stmt (pre_p, gimple_build_label (start_label));

  gimplify_ctxp->exit_label = NULL_TREE;

  gimplify_and_add (LOOP_EXPR_BODY (*expr_p), pre_p);
  gimplify_seq_add_stmt (pre_p, gimple_build_goto (start_label));

  if (gimplify_ctxp->exit_label)
    gimplify_seq_add_stmt (pre_p,
			   gimple_build_label (gimplify_ctxp->exit_label));