Skip to content
Snippets Groups Projects
gimplify.c 444 KiB
Newer Older
      else if (COMPLETE_TYPE_P (TREE_TYPE (*expr_p))
	       && TYPE_MODE (TREE_TYPE (*expr_p)) != BLKmode)
	  /* Historically, the compiler has treated a bare reference
	     to a non-BLKmode volatile lvalue as forcing a load.  */
	  tree type = TYPE_MAIN_VARIANT (TREE_TYPE (*expr_p));
	  /* Normally, we do not want to create a temporary for a
	     TREE_ADDRESSABLE type because such a type should not be
	     copied by bitwise-assignment.  However, we make an
	     exception here, as all we are doing here is ensuring that
	     we read the bytes that make up the type.  We use
	     create_tmp_var_raw because create_tmp_var will abort when
	     given a TREE_ADDRESSABLE type.  */
	  tree tmp = create_tmp_var_raw (type, "vol");
	  gimple_add_tmp_var (tmp);
	  gimplify_assign (tmp, *expr_p, pre_p);
	  *expr_p = NULL;
	}
      else
	/* We can't do anything useful with a volatile reference to
	   an incomplete type, so just throw it away.  Likewise for
	   a BLKmode type, since any implicit inner load should
	   already have been turned into an explicit one by the
	   gimplification process.  */
    }

  /* If we are gimplifying at the statement level, we're done.  Tack
     everything together and return.  */
  if (fallback == fb_none || is_statement)
      /* Since *EXPR_P has been converted into a GIMPLE tuple, clear
         it out for GC to reclaim it.  */
      *expr_p = NULL_TREE;

      if (!gimple_seq_empty_p (internal_pre)
	  || !gimple_seq_empty_p (internal_post))
	  gimplify_seq_add_seq (&internal_pre, internal_post);
	  gimplify_seq_add_seq (pre_p, internal_pre);

      /* The result of gimplifying *EXPR_P is going to be the last few
	 statements in *PRE_P and *POST_P.  Add location information
	 to all the statements that were added by the gimplification
	 helpers.  */
      if (!gimple_seq_empty_p (*pre_p))
	annotate_all_with_location_after (*pre_p, pre_last_gsi, input_location);

      if (!gimple_seq_empty_p (*post_p))
	annotate_all_with_location_after (*post_p, post_last_gsi,
					  input_location);

#ifdef ENABLE_GIMPLE_CHECKING
  if (*expr_p)
    {
      enum tree_code code = TREE_CODE (*expr_p);
      /* These expressions should already be in gimple IR form.  */
      gcc_assert (code != MODIFY_EXPR
		  && code != ASM_EXPR
		  && code != BIND_EXPR
		  && code != CATCH_EXPR
		  && (code != COND_EXPR || gimplify_ctxp->allow_rhs_cond_expr)
		  && code != EH_FILTER_EXPR
		  && code != GOTO_EXPR
		  && code != LABEL_EXPR
		  && code != LOOP_EXPR
		  && code != SWITCH_EXPR
		  && code != TRY_FINALLY_EXPR
		  && code != EH_ELSE_EXPR
		  && code != OACC_PARALLEL
		  && code != OACC_KERNELS
		  && code != OACC_DATA
		  && code != OACC_HOST_DATA
		  && code != OACC_DECLARE
		  && code != OACC_UPDATE
		  && code != OACC_ENTER_DATA
		  && code != OACC_EXIT_DATA
		  && code != OACC_CACHE
		  && code != OMP_CRITICAL
		  && code != OMP_FOR
Jakub Jelinek's avatar
Jakub Jelinek committed
		  && code != OMP_TASKGROUP
		  && code != OMP_ORDERED
		  && code != OMP_PARALLEL
		  && code != OMP_SCAN
		  && code != OMP_SECTIONS
		  && code != OMP_SECTION
		  && code != OMP_SINGLE);
    }
#endif
  /* Otherwise we're gimplifying a subexpression, so the resulting
     value is interesting.  If it's a valid operand that matches
     GIMPLE_TEST_F, we're done. Unless we are handling some
     post-effects internally; if that's the case, we need to copy into
     a temporary before adding the post-effects to POST_P.  */
  if (gimple_seq_empty_p (internal_post) && (*gimple_test_f) (*expr_p))
    goto out;

  /* Otherwise, we need to create a new temporary for the gimplified
     expression.  */

  /* We can't return an lvalue if we have an internal postqueue.  The
     object the lvalue refers to would (probably) be modified by the
     postqueue; we need to copy the value out first, which means an
     rvalue.  */
  if ((fallback & fb_lvalue)
      && gimple_seq_empty_p (internal_post)
      && is_gimple_addressable (*expr_p))
    {
      /* An lvalue will do.  Take the address of the expression, store it
	 in a temporary, and replace the expression with an INDIRECT_REF of
	 that temporary.  */
      tree ref_alias_type = reference_alias_ptr_type (*expr_p);
      unsigned int ref_align = get_object_alignment (*expr_p);
      tree ref_type = TREE_TYPE (*expr_p);
      tmp = build_fold_addr_expr_loc (input_location, *expr_p);
      gimplify_expr (&tmp, pre_p, post_p, is_gimple_reg, fb_rvalue);
      if (TYPE_ALIGN (ref_type) != ref_align)
	ref_type = build_aligned_type (ref_type, ref_align);
      *expr_p = build2 (MEM_REF, ref_type,
			tmp, build_zero_cst (ref_alias_type));
  else if ((fallback & fb_rvalue) && is_gimple_reg_rhs_or_call (*expr_p))
      /* An rvalue will do.  Assign the gimplified expression into a
	 new temporary TMP and replace the original expression with
	 TMP.  First, make sure that the expression has a type so that
	 it can be assigned into a temporary.  */
      gcc_assert (!VOID_TYPE_P (TREE_TYPE (*expr_p)));
      *expr_p = get_formal_tmp_var (*expr_p, pre_p);
      if (!(fallback & fb_mayfail))
	{
	  fprintf (stderr, "gimplification failed:\n");
	  print_generic_expr (stderr, *expr_p);
	  debug_tree (*expr_p);
	  internal_error ("gimplification failed");
	}
#endif
      gcc_assert (fallback & fb_mayfail);
      /* If this is an asm statement, and the user asked for the
	 impossible, don't die.  Fail and let gimplify_asm_expr
      ret = GS_ERROR;
      goto out;
    }

  /* Make sure the temporary matches our predicate.  */
  gcc_assert ((*gimple_test_f) (*expr_p));
  if (!gimple_seq_empty_p (internal_post))
      annotate_all_with_location (internal_post, input_location);
      gimplify_seq_add_seq (pre_p, internal_post);
    }

 out:
  input_location = saved_location;
  return ret;
}

/* Like gimplify_expr but make sure the gimplified result is not itself
   a SSA name (but a decl if it were).  Temporaries required by
   evaluating *EXPR_P may be still SSA names.  */

static enum gimplify_status
gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
	       bool (*gimple_test_f) (tree), fallback_t fallback,
	       bool allow_ssa)
{
  bool was_ssa_name_p = TREE_CODE (*expr_p) == SSA_NAME;
  enum gimplify_status ret = gimplify_expr (expr_p, pre_p, post_p,
					    gimple_test_f, fallback);
  if (! allow_ssa
      && TREE_CODE (*expr_p) == SSA_NAME)
    {
      tree name = *expr_p;
      if (was_ssa_name_p)
	*expr_p = get_initialized_tmp_var (*expr_p, pre_p, NULL, false);
      else
	{
	  /* Avoid the extra copy if possible.  */
	  *expr_p = create_tmp_reg (TREE_TYPE (name));
	  gimple_set_lhs (SSA_NAME_DEF_STMT (name), *expr_p);
	  release_ssa_name (name);
	}
    }
  return ret;
}

/* Look through TYPE for variable-sized objects and gimplify each such
   size that we find.  Add to LIST_P any statements generated.  */
gimplify_type_sizes (tree type, gimple_seq *list_p)
Richard Henderson's avatar
Richard Henderson committed
  tree field, t;

  if (type == NULL || type == error_mark_node)
  /* We first do the main variant, then copy into any other variants.  */
Richard Henderson's avatar
Richard Henderson committed
  type = TYPE_MAIN_VARIANT (type);
  if (TYPE_SIZES_GIMPLIFIED (type))
  switch (TREE_CODE (type))
    {
    case INTEGER_TYPE:
    case ENUMERAL_TYPE:
    case BOOLEAN_TYPE:
    case REAL_TYPE:
    case FIXED_POINT_TYPE:
      gimplify_one_sizepos (&TYPE_MIN_VALUE (type), list_p);
      gimplify_one_sizepos (&TYPE_MAX_VALUE (type), list_p);
Richard Henderson's avatar
Richard Henderson committed

      for (t = TYPE_NEXT_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))
	{
	  TYPE_MIN_VALUE (t) = TYPE_MIN_VALUE (type);
	  TYPE_MAX_VALUE (t) = TYPE_MAX_VALUE (type);
	}
Richard Henderson's avatar
Richard Henderson committed
      /* These types may not have declarations, so handle them here.  */
      gimplify_type_sizes (TREE_TYPE (type), list_p);
      gimplify_type_sizes (TYPE_DOMAIN (type), list_p);
      /* Ensure VLA bounds aren't removed, for -O0 they should be variables
	 with assigned stack slots, for -O1+ -g they should be tracked
	 by VTA.  */
      if (!(TYPE_NAME (type)
	    && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
	    && DECL_IGNORED_P (TYPE_NAME (type)))
	  && TYPE_DOMAIN (type)
	  && INTEGRAL_TYPE_P (TYPE_DOMAIN (type)))
	{
	  t = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
	    DECL_IGNORED_P (t) = 0;
	  t = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
      break;

    case RECORD_TYPE:
    case UNION_TYPE:
    case QUAL_UNION_TYPE:
      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
	  {
	    gimplify_one_sizepos (&DECL_FIELD_OFFSET (field), list_p);
	    gimplify_one_sizepos (&DECL_SIZE (field), list_p);
	    gimplify_one_sizepos (&DECL_SIZE_UNIT (field), list_p);
	    gimplify_type_sizes (TREE_TYPE (field), list_p);
	  }
      break;

    case POINTER_TYPE:
    case REFERENCE_TYPE:
	/* We used to recurse on the pointed-to type here, which turned out to
	   be incorrect because its definition might refer to variables not
	   yet initialized at this point if a forward declaration is involved.

	   It was actually useful for anonymous pointed-to types to ensure
	   that the sizes evaluation dominates every possible later use of the
	   values.  Restricting to such types here would be safe since there
	   is no possible forward declaration around, but would introduce an
	   undesirable middle-end semantic to anonymity.  We then defer to
	   front-ends the responsibility of ensuring that the sizes are
	   evaluated both early and late enough, e.g. by attaching artificial
  gimplify_one_sizepos (&TYPE_SIZE (type), list_p);
  gimplify_one_sizepos (&TYPE_SIZE_UNIT (type), list_p);
Richard Henderson's avatar
Richard Henderson committed
  for (t = TYPE_NEXT_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))
Richard Henderson's avatar
Richard Henderson committed
      TYPE_SIZE (t) = TYPE_SIZE (type);
      TYPE_SIZE_UNIT (t) = TYPE_SIZE_UNIT (type);
      TYPE_SIZES_GIMPLIFIED (t) = 1;
    }
}

/* A subroutine of gimplify_type_sizes to make sure that *EXPR_P,
   a size or position, has had all of its SAVE_EXPRs evaluated.
   We add any required statements to *STMT_P.  */
gimplify_one_sizepos (tree *expr_p, gimple_seq *stmt_p)
  /* We don't do anything if the value isn't there, is constant, or contains
     A PLACEHOLDER_EXPR.  We also don't want to do anything if it's already
     a VAR_DECL.  If it's a VAR_DECL from another function, the gimplifier
     will want to replace it with a new variable, but that will cause problems
     if this type is from outside the function.  It's OK to have that here.  */
  if (expr == NULL_TREE
      || is_gimple_constant (expr)
      || TREE_CODE (expr) == VAR_DECL
      || CONTAINS_PLACEHOLDER_P (expr))
  /* SSA names in decl/type fields are a bad idea - they'll get reclaimed
     if the def vanishes.  */
  gimplify_expr (expr_p, stmt_p, NULL, is_gimple_val, fb_rvalue, false);

  /* If expr wasn't already is_gimple_sizepos or is_gimple_constant from the
     FE, ensure that it is a VAR_DECL, otherwise we might handle some decls
     as gimplify_vla_decl even when they would have all sizes INTEGER_CSTs.  */
  if (is_gimple_constant (*expr_p))
    *expr_p = get_initialized_tmp_var (*expr_p, stmt_p, NULL, false);
/* Gimplify the body of statements of FNDECL and return a GIMPLE_BIND node
   containing the sequence of corresponding GIMPLE statements.  If DO_PARMS
   is true, also gimplify the parameters.  */
gimplify_body (tree fndecl, bool do_parms)
{
  location_t saved_location = input_location;
  gimple_seq parm_stmts, parm_cleanup = NULL, seq;
  gimple *outer_stmt;

  timevar_push (TV_TREE_GIMPLIFY);
  /* Initialize for optimize_insn_for_s{ize,peed}_p possibly called during
     gimplification.  */
  default_rtl_profile ();

Diego Novillo's avatar
Diego Novillo committed
  gcc_assert (gimplify_ctxp == NULL);
  if (flag_openacc || flag_openmp)
Jakub Jelinek's avatar
Jakub Jelinek committed
    {
      gcc_assert (gimplify_omp_ctxp == NULL);
      if (lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl)))
	gimplify_omp_ctxp = new_omp_context (ORT_IMPLICIT_TARGET);
  /* Unshare most shared trees in the body and in that of any nested functions.
     It would seem we don't have to do this for nested functions because
     they are supposed to be output and then the outer function gimplified
     first, but the g++ front end doesn't always do it that way.  */
  unshare_body (fndecl);
  unvisit_body (fndecl);
  /* Make sure input_location isn't set to something weird.  */
  input_location = DECL_SOURCE_LOCATION (fndecl);

  /* Resolve callee-copies.  This has to be done before processing
     the body so that DECL_VALUE_EXPR gets processed correctly.  */
  parm_stmts = do_parms ? gimplify_parameters (&parm_cleanup) : NULL;
  /* Gimplify the function's body.  */
  gimplify_stmt (&DECL_SAVED_TREE (fndecl), &seq);
  outer_stmt = gimple_seq_first_stmt (seq);
  if (!outer_stmt)
      outer_stmt = gimple_build_nop ();
      gimplify_seq_add_stmt (&seq, outer_stmt);
  /* 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_stmt) == GIMPLE_BIND
      && gimple_seq_first (seq) == gimple_seq_last (seq))
    outer_bind = as_a <gbind *> (outer_stmt);
  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));
      if (parm_cleanup)
	{
	  gtry *g = gimple_build_try (parm_stmts, parm_cleanup,
				      GIMPLE_TRY_FINALLY);
	  parm_stmts = NULL;
	  gimple_seq_add_stmt (&parm_stmts, g);
	}
      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 ((flag_openacc || flag_openmp || flag_openmp_simd)
      && gimplify_omp_ctxp)
Jakub Jelinek's avatar
Jakub Jelinek committed
    {
      delete_omp_context (gimplify_omp_ctxp);
      gimplify_omp_ctxp = NULL;
    }

  pop_gimplify_context (outer_bind);
Diego Novillo's avatar
Diego Novillo committed
  gcc_assert (gimplify_ctxp == NULL);
  if (flag_checking && !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.  */

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

static bool
flag_instrument_functions_exclude_p (tree fndecl)
{
  v = (vec<char_p> *) flag_instrument_functions_exclude_functions;
  if (v && v->length () > 0)
      name = lang_hooks.decl_printable_name (fndecl, 1);
  v = (vec<char_p> *) flag_instrument_functions_exclude_files;
  if (v && v->length () > 0)
/* 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));

  if (DECL_STRUCT_FUNCTION (fndecl))
    push_cfun (DECL_STRUCT_FUNCTION (fndecl));
  else
    push_struct_function (fndecl);
  /* Tentatively set PROP_gimple_lva here, and reset it in gimplify_va_arg_expr
     if necessary.  */
  cfun->curr_properties |= PROP_gimple_lva;

  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)
  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
    asan_poisoned_variables = new hash_set<tree> ();
  bind = gimplify_body (fndecl, true);
  if (asan_poisoned_variables)
    {
      delete asan_poisoned_variables;
      asan_poisoned_variables = NULL;
    }

  /* The tree body of the function is no longer needed, replace it
     with the new GIMPLE body.  */
  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)
      /* Do not instrument extern inline functions.  */
      && !(DECL_DECLARED_INLINE_P (fndecl)
	   && DECL_EXTERNAL (fndecl)
	   && DECL_DISREGARD_INLINE_LIMITS (fndecl))
      && !flag_instrument_functions_exclude_p (fndecl))
      gimple *tf;
      gimple_seq cleanup = NULL, body = NULL;
      tree tmp_var, this_fn_addr;
      /* The instrumentation hooks aren't going to call the instrumented
	 function and the address they receive is expected to be matchable
	 against symbol addresses.  Make sure we don't create a trampoline,
	 in case the current function is nested.  */
      this_fn_addr = build_fold_addr_expr (current_function_decl);
      TREE_NO_TRAMPOLINE (this_fn_addr) = 1;

      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, this_fn_addr, 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, this_fn_addr, tmp_var);
      gimplify_seq_add_stmt (&body, call);
      gimplify_seq_add_stmt (&body, tf);
      new_bind = gimple_build_bind (NULL, body, NULL);
      /* Replace the current function body with the body
         wrapped in the try/finally TF.  */
      gimple_seq_add_stmt (&seq, new_bind);
      gimple_set_body (fndecl, seq);
  if (sanitize_flags_p (SANITIZE_THREAD))
    {
      gcall *call = gimple_build_call_internal (IFN_TSAN_FUNC_EXIT, 0);
      gimple *tf = gimple_build_try (seq, call, GIMPLE_TRY_FINALLY);
      gbind *new_bind = gimple_build_bind (NULL, tf, NULL);
      /* Replace the current function body with the body
	 wrapped in the try/finally TF.  */
      seq = NULL;
      gimple_seq_add_stmt (&seq, new_bind);
      gimple_set_body (fndecl, seq);
  DECL_SAVED_TREE (fndecl) = NULL_TREE;
  cfun->curr_properties |= PROP_gimple_any;
  dump_function (TDI_gimple, fndecl);
/* Return a dummy expression of type TYPE in order to keep going after an
   error.  */
static tree
dummy_object (tree type)
  tree t = build_int_cst (build_pointer_type (type), 0);
  return build2 (MEM_REF, type, t, t);
/* Gimplify __builtin_va_arg, aka VA_ARG_EXPR, which is not really a
   builtin function, but a very special sort of operator.  */
gimplify_va_arg_expr (tree *expr_p, gimple_seq *pre_p,
		      gimple_seq *post_p ATTRIBUTE_UNUSED)
{
  tree promoted_type, have_va_type;
  tree valist = TREE_OPERAND (*expr_p, 0);
  tree type = TREE_TYPE (*expr_p);
  location_t loc = EXPR_LOCATION (*expr_p);
  /* Verify that valist is of the proper type.  */
  have_va_type = TREE_TYPE (valist);
  if (have_va_type == error_mark_node)
    return GS_ERROR;
  have_va_type = targetm.canonical_va_list_type (have_va_type);
  if (have_va_type == NULL_TREE
    /* Handle 'Case 1: Not an array type' from c-common.c/build_va_arg.  */
    have_va_type
      = targetm.canonical_va_list_type (TREE_TYPE (TREE_TYPE (valist)));
  gcc_assert (have_va_type != NULL_TREE);
  /* Generate a diagnostic for requesting data of a type that cannot
     be passed through `...' due to type promotion at the call site.  */
  if ((promoted_type = lang_hooks.types.type_promotes_to (type))
	   != type)
    {
      static bool gave_help;
      bool warned;
      /* Use the expansion point to handle cases such as passing bool (defined
	 in a system header) through `...'.  */
	= expansion_point_location_if_in_system_header (loc);
      /* Unfortunately, this is merely undefined, rather than a constraint
	 violation, so we cannot make this an error.  If this call is never
	 executed, the program is still strictly conforming.  */
      warned = warning_at (xloc, 0,
			   "%qT is promoted to %qT when passed through %<...%>",
			   type, promoted_type);
      if (!gave_help && warned)
	{
	  gave_help = true;
	  inform (xloc, "(so you should pass %qT not %qT to %<va_arg%>)",
      /* We can, however, treat "undefined" any way we please.
	 Call abort to encourage the user to fix the program.  */
      if (warned)
	inform (xloc, "if this code is reached, the program will abort");
      /* Before the abort, allow the evaluation of the va_list
	 expression to exit or longjmp.  */
      gimplify_and_add (valist, pre_p);
      t = build_call_expr_loc (loc,
			       builtin_decl_implicit (BUILT_IN_TRAP), 0);
      gimplify_and_add (t, pre_p);

      /* This is dead code, but go ahead and finish so that the
	 mode of the result comes out right.  */
      *expr_p = dummy_object (type);
      return GS_ALL_DONE;
  tag = build_int_cst (build_pointer_type (type), 0);
  aptag = build_int_cst (TREE_TYPE (valist), 0);

  *expr_p = build_call_expr_internal_loc (loc, IFN_VA_ARG, type, 3,
					  valist, tag, aptag);
  /* Clear the tentatively set PROP_gimple_lva, to indicate that IFN_VA_ARG
     needs to be expanded.  */
  cfun->curr_properties &= ~PROP_gimple_lva;

/* Build a new GIMPLE_ASSIGN tuple and append it to the end of *SEQ_P.

   DST/SRC are the destination and source respectively.  You can pass
   ungimplified trees in DST or SRC, in which case they will be
   converted to a gimple operand if necessary.

   This function returns the newly created GIMPLE_ASSIGN tuple.  */

gimplify_assign (tree dst, tree src, gimple_seq *seq_p)
{
  tree t = build2 (MODIFY_EXPR, TREE_TYPE (dst), dst, src);
  gimplify_and_add (t, seq_p);
  ggc_free (t);
  return gimple_seq_last_stmt (*seq_p);
}

Andrew MacLeod's avatar
Andrew MacLeod committed
inline hashval_t
gimplify_hasher::hash (const elt_t *p)
Andrew MacLeod's avatar
Andrew MacLeod committed
{
  tree t = p->val;
  return iterative_hash_expr (t, 0);
}

inline bool
gimplify_hasher::equal (const elt_t *p1, const elt_t *p2)
Andrew MacLeod's avatar
Andrew MacLeod committed
{
  tree t1 = p1->val;
  tree t2 = p2->val;
  enum tree_code code = TREE_CODE (t1);

  if (TREE_CODE (t2) != code
      || TREE_TYPE (t1) != TREE_TYPE (t2))
    return false;

  if (!operand_equal_p (t1, t2, 0))
    return false;

  /* Only allow them to compare equal if they also hash equal; otherwise
     results are nondeterminate, and we fail bootstrap comparison.  */
  gcc_checking_assert (hash (p1) == hash (p2));