On Mon, Nov 7, 2011 at 4:57 PM, Michael Matz <[email protected]> wrote:
> Hi,
>
> On Thu, 3 Nov 2011, Richard Guenther wrote:
>
>> Otherwise the patch looks ok, but given the Ada issue let's wait until
>> that is sorted out in some way. That also gives others the chance to
>> comment on the patch.
>
> So, this is what I came up with. As discussed I didn't do the uintptr_t
> case and didn't use auto_var_in_fn_p (but added a DECL_CONTEXT check).
> I've added the gimple_clobber_p helper and used it in some places and now
> dump the clobbers as
> lhs ={v} {CLOBBER}
>
> I haven't yet worked on the whole try/finally optimizations.
>
> Regstrapped (all default langs, now with Ada) on x86_64-linux. Okay for
> trunk?
Ok.
Thanks,
Richard.
> Ciao,
> Michael.
>
> * gengtype.c (write_field_root): Avoid out-of-scope access of newv.
>
> * tree-stdarg.c (execute_optimize_stdarg): Accept clobbers.
>
> * tree.h (TREE_CLOBBER_P): New macro.
> * gimple.h (gimple_clobber_p): New inline function.
> * gimplify.c (gimplify_bind_expr): Add clobbers for all variables
> that go out of scope and live in memory.
> * tree-ssa-operands.c (get_expr_operands): Transfer volatility also
> for constructors.
> * cfgexpand.c (decl_to_stack_part): New static variable.
> (add_stack_var): Allocate it, and remember mapping.
> (fini_vars_expansion): Deallocate it.
> (stack_var_conflict_p): Add early outs.
> (visit_op, visit_conflict, add_scope_conflicts_1,
> add_scope_conflicts): New static functions.
> (expand_used_vars_for_block): Don't call add_stack_var_conflict, tidy.
> (expand_used_vars): Add scope conflicts.
> (expand_gimple_stmt_1): Expand clobbers to nothing.
> (expand_debug_expr): Ditto.
>
> * tree-pretty-print.c (dump_generic_node): Dump clobbers nicely.
> * tree-ssa-live.c (remove_unused_locals): Remove clobbers that
> refer to otherwise unused locals.
> * tree-sra.c (build_accesses_from_assign): Ignore clobbers.
> * tree-ssa-dce.c (mark_stmt_if_obviously_necessary): Clobbers of
> SSA names aren't necessary.
> (propagate_necessity): Accept and ignore constructors on the rhs,
> tidy.
> * gimple.c (walk_gimple_op): Accept constructors like mem_rhs.
> * tree-ssa-structalias.c (find_func_aliases): Clobbers don't store
> any known value.
> * tree-ssa-sccvn.c (vn_reference_lookup_3): Ditto, in particular they
> don't zero-initialize something.
> * tree-ssa-phiopt.c (cond_if_else_store_replacement_1): Ignore
> clobber RHS, we don't want PHI nodes with those.
>
> testsuite/
> * gcc.dg/tree-ssa/20031015-1.c: Adjust.
> * g++.dg/tree-ssa/ehcleanup-1.C: Ditto.
> * g++.dg/eh/builtin1.C: Rewrite to not use local variables.
> * g++.dg/eh/builtin2.C: Ditto.
> * g++.dg/eh/builtin3.C: Ditto.
>
> Index: gengtype.c
> ===================================================================
> --- gengtype.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ gengtype.c 2011-11-07 16:12:35.000000000 +0100
> @@ -3650,14 +3650,13 @@ write_field_root (outf_p f, pair_p v, ty
> int has_length, struct fileloc *line, const char *if_marked,
> bool emit_pch, type_p field_type, const char *field_name)
> {
> + struct pair newv;
> /* If the field reference is relative to V, rather than to some
> subcomponent of V, we can mark any subarrays with a single stride.
> We're effectively treating the field as a global variable in its
> own right. */
> if (v && type == v->type)
> {
> - struct pair newv;
> -
> newv = *v;
> newv.type = field_type;
> newv.name = ACONCAT ((v->name, ".", field_name, NULL));
> Index: tree.h
> ===================================================================
> --- tree.h (revision 180833)
> +++ tree.h (working copy)
> @@ -1637,6 +1637,14 @@ struct GTY(()) tree_vec {
> #define CONSTRUCTOR_BITFIELD_P(NODE) \
> (DECL_BIT_FIELD (FIELD_DECL_CHECK (NODE)) && DECL_MODE (NODE) != BLKmode)
>
> +/* True if NODE is a clobber right hand side, an expression of indeterminate
> + value that clobbers the LHS in a copy instruction. We use a volatile
> + empty CONSTRUCTOR for this, as it matches most of the necessary semantic.
> + In particular the volatile flag causes us to not prematurely remove
> + such clobber instructions. */
> +#define TREE_CLOBBER_P(NODE) \
> + (TREE_CODE (NODE) == CONSTRUCTOR && TREE_THIS_VOLATILE (NODE))
> +
> /* A single element of a CONSTRUCTOR. VALUE holds the actual value of the
> element. INDEX can optionally design the position of VALUE: in arrays,
> it is the index where VALUE has to be placed; in structures, it is the
> Index: gimple.h
> ===================================================================
> --- gimple.h.orig 2011-11-07 15:56:25.000000000 +0100
> +++ gimple.h 2011-11-07 16:12:35.000000000 +0100
> @@ -2000,6 +2000,14 @@ gimple_assign_cast_p (gimple s)
> return false;
> }
>
> +/* Return true if S is a clobber statement. */
> +
> +static inline bool
> +gimple_clobber_p (gimple s)
> +{
> + return gimple_assign_single_p (s)
> + && TREE_CLOBBER_P (gimple_assign_rhs1 (s));
> +}
>
> /* Return true if GS is a GIMPLE_CALL. */
>
> Index: gimplify.c
> ===================================================================
> --- gimplify.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ gimplify.c 2011-11-07 16:12:35.000000000 +0100
> @@ -1127,7 +1127,8 @@ gimplify_bind_expr (tree *expr_p, gimple
> bool old_save_stack = gimplify_ctxp->save_stack;
> tree t;
> gimple gimple_bind;
> - gimple_seq body;
> + gimple_seq body, cleanup;
> + gimple stack_save;
>
> tree temp = voidify_wrapper_expr (bind_expr, NULL);
>
> @@ -1173,22 +1174,50 @@ gimplify_bind_expr (tree *expr_p, gimple
> gimplify_stmt (&BIND_EXPR_BODY (bind_expr), &body);
> gimple_bind_set_body (gimple_bind, body);
>
> + cleanup = NULL;
> + stack_save = NULL;
> if (gimplify_ctxp->save_stack)
> {
> - gimple stack_save, stack_restore, gs;
> - gimple_seq cleanup, new_body;
> + gimple stack_restore;
>
> /* Save stack on entry and restore it on exit. Add a try_finally
> block to achieve this. Note that mudflap depends on the
> format of the emitted code: see mx_register_decls(). */
> build_stack_save_restore (&stack_save, &stack_restore);
>
> - cleanup = new_body = NULL;
> 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))
> + {
> + if (TREE_CODE (t) == VAR_DECL
> + && !is_global_var (t)
> + && DECL_CONTEXT (t) == current_function_decl
> + && !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.
> */
> + && needs_to_live_in_memory (t))
> + {
> + tree clobber = build_constructor (TREE_TYPE (t), NULL);
> + TREE_THIS_VOLATILE (clobber) = 1;
> + gimplify_seq_add_stmt (&cleanup, gimple_build_assign (t, clobber));
> + }
> + }
> +
> + if (cleanup)
> + {
> + gimple gs;
> + gimple_seq new_body;
> +
> + new_body = NULL;
> gs = gimple_build_try (gimple_bind_body (gimple_bind), cleanup,
> GIMPLE_TRY_FINALLY);
>
> - gimplify_seq_add_stmt (&new_body, stack_save);
> + if (stack_save)
> + gimplify_seq_add_stmt (&new_body, stack_save);
> gimplify_seq_add_stmt (&new_body, gs);
> gimple_bind_set_body (gimple_bind, new_body);
> }
> Index: cfgexpand.c
> ===================================================================
> --- cfgexpand.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ cfgexpand.c 2011-11-07 16:12:35.000000000 +0100
> @@ -135,7 +135,7 @@ set_rtl (tree t, rtx x)
> /* If we don't yet have something recorded, just record it now. */
> if (!DECL_RTL_SET_P (var))
> SET_DECL_RTL (var, x);
> - /* If we have it set alrady to "multiple places" don't
> + /* If we have it set already to "multiple places" don't
> change this. */
> else if (DECL_RTL (var) == pc_rtx)
> ;
> @@ -184,6 +184,7 @@ struct stack_var
> static struct stack_var *stack_vars;
> static size_t stack_vars_alloc;
> static size_t stack_vars_num;
> +static struct pointer_map_t *decl_to_stack_part;
>
> /* An array of indices such that stack_vars[stack_vars_sorted[i]].size
> is non-decreasing. */
> @@ -262,7 +263,11 @@ add_stack_var (tree decl)
> stack_vars
> = XRESIZEVEC (struct stack_var, stack_vars, stack_vars_alloc);
> }
> + if (!decl_to_stack_part)
> + decl_to_stack_part = pointer_map_create ();
> +
> v = &stack_vars[stack_vars_num];
> + * (size_t *)pointer_map_insert (decl_to_stack_part, decl) = stack_vars_num;
>
> v->decl = decl;
> v->size = tree_low_cst (DECL_SIZE_UNIT (SSAVAR (decl)), 1);
> @@ -309,6 +314,14 @@ stack_var_conflict_p (size_t x, size_t y
> {
> struct stack_var *a = &stack_vars[x];
> struct stack_var *b = &stack_vars[y];
> + if (x == y)
> + return false;
> + /* Partitions containing an SSA name result from gimple registers
> + with things like unsupported modes. They are top-level and
> + hence conflict with everything else. */
> + if (TREE_CODE (a->decl) == SSA_NAME || TREE_CODE (b->decl) == SSA_NAME)
> + return true;
> +
> if (!a->conflicts || !b->conflicts)
> return false;
> return bitmap_bit_p (a->conflicts, y);
> @@ -379,6 +392,163 @@ add_alias_set_conflicts (void)
> }
> }
>
> +/* Callback for walk_stmt_ops. If OP is a decl touched by add_stack_var
> + enter its partition number into bitmap DATA. */
> +
> +static bool
> +visit_op (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
> +{
> + bitmap active = (bitmap)data;
> + op = get_base_address (op);
> + if (op
> + && DECL_P (op)
> + && DECL_RTL_IF_SET (op) == pc_rtx)
> + {
> + size_t *v = (size_t *) pointer_map_contains (decl_to_stack_part, op);
> + if (v)
> + bitmap_set_bit (active, *v);
> + }
> + return false;
> +}
> +
> +/* Callback for walk_stmt_ops. If OP is a decl touched by add_stack_var
> + record conflicts between it and all currently active other partitions
> + from bitmap DATA. */
> +
> +static bool
> +visit_conflict (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
> +{
> + bitmap active = (bitmap)data;
> + op = get_base_address (op);
> + if (op
> + && DECL_P (op)
> + && DECL_RTL_IF_SET (op) == pc_rtx)
> + {
> + size_t *v =
> + (size_t *) pointer_map_contains (decl_to_stack_part, op);
> + if (v && bitmap_set_bit (active, *v))
> + {
> + size_t num = *v;
> + bitmap_iterator bi;
> + unsigned i;
> + gcc_assert (num < stack_vars_num);
> + EXECUTE_IF_SET_IN_BITMAP (active, 0, i, bi)
> + add_stack_var_conflict (num, i);
> + }
> + }
> + return false;
> +}
> +
> +/* Helper routine for add_scope_conflicts, calculating the active partitions
> + at the end of BB, leaving the result in WORK. We're called to generate
> + conflicts when FOR_CONFLICT is true, otherwise we're just tracking
> + liveness. */
> +
> +static void
> +add_scope_conflicts_1 (basic_block bb, bitmap work, bool for_conflict)
> +{
> + edge e;
> + edge_iterator ei;
> + gimple_stmt_iterator gsi;
> + bool (*visit)(gimple, tree, void *);
> +
> + bitmap_clear (work);
> + FOR_EACH_EDGE (e, ei, bb->preds)
> + bitmap_ior_into (work, (bitmap)e->src->aux);
> +
> + if (for_conflict)
> + {
> + /* We need to add conflicts for everything life at the start of
> + this block. Unlike classical lifeness for named objects we can't
> + rely on seeing a def/use of the names we're interested in.
> + There might merely be indirect loads/stores. We'd not add any
> + conflicts for such partitions. */
> + bitmap_iterator bi;
> + unsigned i;
> + EXECUTE_IF_SET_IN_BITMAP (work, 0, i, bi)
> + {
> + unsigned j;
> + bitmap_iterator bj;
> + EXECUTE_IF_SET_IN_BITMAP (work, i, j, bj)
> + add_stack_var_conflict (i, j);
> + }
> + visit = visit_conflict;
> + }
> + else
> + visit = visit_op;
> +
> + for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> + {
> + gimple stmt = gsi_stmt (gsi);
> + if (!is_gimple_debug (stmt))
> + walk_stmt_load_store_addr_ops (stmt, work, visit, visit, visit);
> + }
> + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> + {
> + gimple stmt = gsi_stmt (gsi);
> +
> + if (gimple_clobber_p (stmt))
> + {
> + tree lhs = gimple_assign_lhs (stmt);
> + size_t *v;
> + /* Nested function lowering might introduce LHSs
> + that are COMPONENT_REFs. */
> + if (TREE_CODE (lhs) != VAR_DECL)
> + continue;
> + if (DECL_RTL_IF_SET (lhs) == pc_rtx
> + && (v = (size_t *)
> + pointer_map_contains (decl_to_stack_part, lhs)))
> + bitmap_clear_bit (work, *v);
> + }
> + else if (!is_gimple_debug (stmt))
> + walk_stmt_load_store_addr_ops (stmt, work, visit, visit, visit);
> + }
> +}
> +
> +/* Generate stack partition conflicts between all partitions that are
> + simultaneously live. */
> +
> +static void
> +add_scope_conflicts (void)
> +{
> + basic_block bb;
> + bool changed;
> + bitmap work = BITMAP_ALLOC (NULL);
> +
> + /* We approximate the life range of a stack variable by taking the first
> + mention of its name as starting point(s), and by the end-of-scope
> + death clobber added by gimplify as ending point(s) of the range.
> + This overapproximates in the case we for instance moved an address-taken
> + operation upward, without also moving a dereference to it upwards.
> + But it's conservatively correct as a variable never can hold values
> + before its name is mentioned at least once.
> +
> + We then do a mostly classical bitmap lifeness algorithm. */
> +
> + FOR_ALL_BB (bb)
> + bb->aux = BITMAP_ALLOC (NULL);
> +
> + changed = true;
> + while (changed)
> + {
> + changed = false;
> + FOR_EACH_BB (bb)
> + {
> + bitmap active = (bitmap)bb->aux;
> + add_scope_conflicts_1 (bb, work, false);
> + if (bitmap_ior_into (active, work))
> + changed = true;
> + }
> + }
> +
> + FOR_EACH_BB (bb)
> + add_scope_conflicts_1 (bb, work, true);
> +
> + BITMAP_FREE (work);
> + FOR_ALL_BB (bb)
> + BITMAP_FREE (bb->aux);
> +}
> +
> /* A subroutine of partition_stack_vars. A comparison function for qsort,
> sorting an array of indices by the properties of the object. */
>
> @@ -1095,11 +1265,8 @@ expand_one_var (tree var, bool toplevel,
> static void
> expand_used_vars_for_block (tree block, bool toplevel)
> {
> - size_t i, j, old_sv_num, this_sv_num, new_sv_num;
> tree t;
>
> - old_sv_num = toplevel ? 0 : stack_vars_num;
> -
> /* Expand all variables at this level. */
> for (t = BLOCK_VARS (block); t ; t = DECL_CHAIN (t))
> if (TREE_USED (t)
> @@ -1107,24 +1274,9 @@ expand_used_vars_for_block (tree block,
> || !DECL_NONSHAREABLE (t)))
> expand_one_var (t, toplevel, true);
>
> - this_sv_num = stack_vars_num;
> -
> /* Expand all variables at containing levels. */
> for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t))
> expand_used_vars_for_block (t, false);
> -
> - /* Since we do not track exact variable lifetimes (which is not even
> - possible for variables whose address escapes), we mirror the block
> - tree in the interference graph. Here we cause all variables at this
> - level, and all sublevels, to conflict. */
> - if (old_sv_num < this_sv_num)
> - {
> - new_sv_num = stack_vars_num;
> -
> - for (i = old_sv_num; i < new_sv_num; ++i)
> - for (j = i < this_sv_num ? i : this_sv_num; j-- > old_sv_num ;)
> - add_stack_var_conflict (i, j);
> - }
> }
>
> /* A subroutine of expand_used_vars. Walk down through the BLOCK tree
> @@ -1312,6 +1464,8 @@ fini_vars_expansion (void)
> XDELETEVEC (stack_vars_sorted);
> stack_vars = NULL;
> stack_vars_alloc = stack_vars_num = 0;
> + pointer_map_destroy (decl_to_stack_part);
> + decl_to_stack_part = NULL;
> }
>
> /* Make a fair guess for the size of the stack frame of the function
> @@ -1466,6 +1620,7 @@ expand_used_vars (void)
>
> if (stack_vars_num > 0)
> {
> + add_scope_conflicts ();
> /* Due to the way alias sets work, no variables with non-conflicting
> alias sets may be assigned the same address. Add conflicts to
> reflect this. */
> @@ -1974,8 +2129,13 @@ expand_gimple_stmt_1 (gimple stmt)
> == GIMPLE_SINGLE_RHS);
> if (gimple_has_location (stmt) && CAN_HAVE_LOCATION_P (rhs))
> SET_EXPR_LOCATION (rhs, gimple_location (stmt));
> - expand_assignment (lhs, rhs,
> - gimple_assign_nontemporal_move_p (stmt));
> + if (TREE_CLOBBER_P (rhs))
> + /* This is a clobber to mark the going out of scope for
> + this LHS. */
> + ;
> + else
> + expand_assignment (lhs, rhs,
> + gimple_assign_nontemporal_move_p (stmt));
> }
> else
> {
> @@ -3165,7 +3325,9 @@ expand_debug_expr (tree exp)
> /* Fall through. */
>
> case CONSTRUCTOR:
> - if (TREE_CODE (TREE_TYPE (exp)) == VECTOR_TYPE)
> + if (TREE_CLOBBER_P (exp))
> + return NULL;
> + else if (TREE_CODE (TREE_TYPE (exp)) == VECTOR_TYPE)
> {
> unsigned i;
> tree val;
> Index: tree-pretty-print.c
> ===================================================================
> --- tree-pretty-print.c.orig 2011-10-27 14:48:01.000000000 +0200
> +++ tree-pretty-print.c 2011-11-07 16:19:17.000000000 +0100
> @@ -1271,8 +1271,10 @@ dump_generic_node (pretty_printer *buffe
> bool is_array_init = false;
> double_int curidx = double_int_zero;
> pp_character (buffer, '{');
> - if (TREE_CODE (TREE_TYPE (node)) == RECORD_TYPE
> - || TREE_CODE (TREE_TYPE (node)) == UNION_TYPE)
> + if (TREE_CLOBBER_P (node))
> + pp_string (buffer, "CLOBBER");
> + else if (TREE_CODE (TREE_TYPE (node)) == RECORD_TYPE
> + || TREE_CODE (TREE_TYPE (node)) == UNION_TYPE)
> is_struct_init = true;
> else if (TREE_CODE (TREE_TYPE (node)) == ARRAY_TYPE
> && TYPE_DOMAIN (TREE_TYPE (node))
> Index: tree-stdarg.c
> ===================================================================
> --- tree-stdarg.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ tree-stdarg.c 2011-11-07 16:12:35.000000000 +0100
> @@ -872,8 +872,11 @@ execute_optimize_stdarg (void)
> if (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
> == GIMPLE_SINGLE_RHS)
> {
> + /* Check for ap ={v} {}. */
> + if (TREE_CLOBBER_P (rhs))
> + continue;
> /* Check for ap[0].field = temp. */
> - if (va_list_counter_struct_op (&si, lhs, rhs, true))
> + else if (va_list_counter_struct_op (&si, lhs, rhs,
> true))
> continue;
>
> /* Check for temp = ap[0].field. */
> Index: tree-ssa-live.c
> ===================================================================
> --- tree-ssa-live.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ tree-ssa-live.c 2011-11-07 16:12:35.000000000 +0100
> @@ -688,6 +688,7 @@ remove_unused_locals (void)
> referenced_var_iterator rvi;
> bitmap global_unused_vars = NULL;
> unsigned srcidx, dstidx, num;
> + bool have_local_clobbers = false;
>
> /* Removing declarations from lexical blocks when not optimizing is
> not only a waste of time, it actually causes differences in stack
> @@ -720,6 +721,12 @@ remove_unused_locals (void)
> if (is_gimple_debug (stmt))
> continue;
>
> + if (gimple_clobber_p (stmt))
> + {
> + have_local_clobbers = true;
> + continue;
> + }
> +
> if (b)
> TREE_USED (b) = true;
>
> @@ -753,6 +760,41 @@ remove_unused_locals (void)
> TREE_USED (e->goto_block) = true;
> }
>
> + /* We do a two-pass approach about the out-of-scope clobbers. We want
> + to remove them if they are the only references to a local variable,
> + but we want to retain them when there's any other. So the first pass
> + ignores them, and the second pass (if there were any) tries to remove
> + them. */
> + if (have_local_clobbers)
> + FOR_EACH_BB (bb)
> + {
> + gimple_stmt_iterator gsi;
> +
> + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
> + {
> + gimple stmt = gsi_stmt (gsi);
> + tree b = gimple_block (stmt);
> +
> + if (gimple_clobber_p (stmt))
> + {
> + tree lhs = gimple_assign_lhs (stmt);
> + lhs = get_base_address (lhs);
> + if (TREE_CODE (lhs) == SSA_NAME)
> + lhs = SSA_NAME_VAR (lhs);
> + if (DECL_P (lhs) && (!var_ann (lhs) || !is_used_p (lhs)))
> + {
> + unlink_stmt_vdef (stmt);
> + gsi_remove (&gsi, true);
> + release_defs (stmt);
> + continue;
> + }
> + if (b)
> + TREE_USED (b) = true;
> + }
> + gsi_next (&gsi);
> + }
> + }
> +
> cfun->has_local_explicit_reg_vars = false;
>
> /* Remove unmarked local vars from local_decls. */
> Index: tree-sra.c
> ===================================================================
> --- tree-sra.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ tree-sra.c 2011-11-07 16:12:35.000000000 +0100
> @@ -1103,7 +1103,9 @@ build_accesses_from_assign (gimple stmt)
> tree lhs, rhs;
> struct access *lacc, *racc;
>
> - if (!gimple_assign_single_p (stmt))
> + if (!gimple_assign_single_p (stmt)
> + /* Scope clobbers don't influence scalarization. */
> + || gimple_clobber_p (stmt))
> return false;
>
> lhs = gimple_assign_lhs (stmt);
> Index: tree-ssa-dce.c
> ===================================================================
> --- tree-ssa-dce.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ tree-ssa-dce.c 2011-11-07 16:12:35.000000000 +0100
> @@ -351,6 +351,12 @@ mark_stmt_if_obviously_necessary (gimple
> mark_stmt_necessary (stmt, true);
> break;
>
> + case GIMPLE_ASSIGN:
> + if (TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME
> + && TREE_CLOBBER_P (gimple_assign_rhs1 (stmt)))
> + return;
> + break;
> +
> default:
> break;
> }
> @@ -917,19 +923,17 @@ propagate_necessity (struct edge_list *e
> else if (gimple_assign_single_p (stmt))
> {
> tree rhs;
> - bool rhs_aliased = false;
> /* If this is a load mark things necessary. */
> rhs = gimple_assign_rhs1 (stmt);
> if (TREE_CODE (rhs) != SSA_NAME
> - && !is_gimple_min_invariant (rhs))
> + && !is_gimple_min_invariant (rhs)
> + && TREE_CODE (rhs) != CONSTRUCTOR)
> {
> if (!ref_may_be_aliased (rhs))
> mark_aliased_reaching_defs_necessary (stmt, rhs);
> else
> - rhs_aliased = true;
> + mark_all_reaching_defs_necessary (stmt);
> }
> - if (rhs_aliased)
> - mark_all_reaching_defs_necessary (stmt);
> }
> else if (gimple_code (stmt) == GIMPLE_RETURN)
> {
> @@ -937,7 +941,8 @@ propagate_necessity (struct edge_list *e
> /* A return statement may perform a load. */
> if (rhs
> && TREE_CODE (rhs) != SSA_NAME
> - && !is_gimple_min_invariant (rhs))
> + && !is_gimple_min_invariant (rhs)
> + && TREE_CODE (rhs) != CONSTRUCTOR)
> {
> if (!ref_may_be_aliased (rhs))
> mark_aliased_reaching_defs_necessary (stmt, rhs);
> @@ -955,6 +960,7 @@ propagate_necessity (struct edge_list *e
> tree op = TREE_VALUE (gimple_asm_input_op (stmt, i));
> if (TREE_CODE (op) != SSA_NAME
> && !is_gimple_min_invariant (op)
> + && TREE_CODE (op) != CONSTRUCTOR
> && !ref_may_be_aliased (op))
> mark_aliased_reaching_defs_necessary (stmt, op);
> }
> Index: gimple.c
> ===================================================================
> --- gimple.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ gimple.c 2011-11-07 16:12:35.000000000 +0100
> @@ -1471,7 +1471,9 @@ walk_gimple_op (gimple stmt, walk_tree_f
> {
> /* If the RHS has more than 1 operand, it is not appropriate
> for the memory. */
> - wi->val_only = !is_gimple_mem_rhs (gimple_assign_rhs1 (stmt))
> + wi->val_only = !(is_gimple_mem_rhs (gimple_assign_rhs1 (stmt))
> + || TREE_CODE (gimple_assign_rhs1 (stmt))
> + == CONSTRUCTOR)
> || !gimple_assign_single_p (stmt);
> wi->is_lhs = true;
> }
> Index: tree-ssa-structalias.c
> ===================================================================
> --- tree-ssa-structalias.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ tree-ssa-structalias.c 2011-11-07 16:12:35.000000000 +0100
> @@ -4437,7 +4437,11 @@ find_func_aliases (gimple origt)
> tree lhsop = gimple_assign_lhs (t);
> tree rhsop = (gimple_num_ops (t) == 2) ? gimple_assign_rhs1 (t) : NULL;
>
> - if (rhsop && AGGREGATE_TYPE_P (TREE_TYPE (lhsop)))
> + if (rhsop && TREE_CLOBBER_P (rhsop))
> + /* Ignore clobbers, they don't actually store anything into
> + the LHS. */
> + ;
> + else if (rhsop && AGGREGATE_TYPE_P (TREE_TYPE (lhsop)))
> do_structure_copy (lhsop, rhsop);
> else
> {
> Index: tree-ssa-reassoc.c
> ===================================================================
> --- tree-ssa-reassoc.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ tree-ssa-reassoc.c 2011-11-07 16:12:35.000000000 +0100
> @@ -2869,6 +2869,12 @@ reassociate_bb (basic_block bb)
> rhs1 = gimple_assign_rhs1 (stmt);
> rhs2 = gimple_assign_rhs2 (stmt);
>
> + /* We don't want to destroy reduction like patterns
> + with reassociation, simply don't start at such
> + statements. */
> + if (is_phi_for_stmt (stmt, rhs1) || is_phi_for_stmt (stmt, rhs2))
> + continue;
> +
> /* For non-bit or min/max operations we can't associate
> all types. Verify that here. */
> if (rhs_code != BIT_IOR_EXPR
> Index: tree-ssa-operands.c
> ===================================================================
> --- tree-ssa-operands.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ tree-ssa-operands.c 2011-11-07 16:12:35.000000000 +0100
> @@ -956,6 +956,12 @@ get_expr_operands (gimple stmt, tree *ex
> constructor_elt *ce;
> unsigned HOST_WIDE_INT idx;
>
> + /* A volatile constructor is actually TREE_CLOBBER_P, transfer
> + the volatility to the statement, don't use TREE_CLOBBER_P for
> + mirroring the other uses of THIS_VOLATILE in this file. */
> + if (TREE_THIS_VOLATILE (expr))
> + gimple_set_has_volatile_ops (stmt, true);
> +
> for (idx = 0;
> VEC_iterate (constructor_elt, CONSTRUCTOR_ELTS (expr), idx, ce);
> idx++)
> Index: tree-ssa-sccvn.c
> ===================================================================
> --- tree-ssa-sccvn.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ tree-ssa-sccvn.c 2011-11-07 16:12:35.000000000 +0100
> @@ -1388,8 +1388,12 @@ vn_reference_lookup_3 (ao_ref *ref, tree
> if (maxsize == -1)
> return (void *)-1;
>
> + /* We can't deduce anything useful from clobbers. */
> + if (gimple_clobber_p (def_stmt))
> + return (void *)-1;
> +
> /* def_stmt may-defs *ref. See if we can derive a value for *ref
> - from that defintion.
> + from that definition.
> 1) Memset. */
> if (is_gimple_reg_type (vr->type)
> && gimple_call_builtin_p (def_stmt, BUILT_IN_MEMSET)
> Index: tree-ssa-phiopt.c
> ===================================================================
> --- tree-ssa-phiopt.c.orig 2011-11-07 15:56:25.000000000 +0100
> +++ tree-ssa-phiopt.c 2011-11-07 16:12:35.000000000 +0100
> @@ -1318,8 +1318,10 @@ cond_if_else_store_replacement_1 (basic_
>
> if (then_assign == NULL
> || !gimple_assign_single_p (then_assign)
> + || gimple_clobber_p (then_assign)
> || else_assign == NULL
> - || !gimple_assign_single_p (else_assign))
> + || !gimple_assign_single_p (else_assign)
> + || gimple_clobber_p (else_assign))
> return false;
>
> lhs = gimple_assign_lhs (then_assign);
> Index: testsuite/gcc.dg/tree-ssa/20031015-1.c
> ===================================================================
> --- testsuite/gcc.dg/tree-ssa/20031015-1.c.orig 2011-11-07 15:56:25.000000000
> +0100
> +++ testsuite/gcc.dg/tree-ssa/20031015-1.c 2011-11-07 16:12:35.000000000
> +0100
> @@ -13,6 +13,6 @@ main(void)
> return 0;
> }
>
> -/* The VDEF comes from the initial assignment and the asm. */
> -/* { dg-final { scan-tree-dump-times "DEF" 2 "alias" } } */
> +/* The VDEF comes from the initial assignment, the asm, and the clobber. */
> +/* { dg-final { scan-tree-dump-times "DEF" 3 "alias" } } */
> /* { dg-final { cleanup-tree-dump "alias" } } */
> Index: testsuite/g++.dg/tree-ssa/ehcleanup-1.C
> ===================================================================
> --- testsuite/g++.dg/tree-ssa/ehcleanup-1.C.orig 2011-11-07
> 15:56:25.000000000 +0100
> +++ testsuite/g++.dg/tree-ssa/ehcleanup-1.C 2011-11-07 16:12:35.000000000
> +0100
> @@ -16,9 +16,9 @@ t (void)
> can_throw ();
> }
> // We ought to remove implicit cleanup, since destructor is empty.
> -// { dg-final { scan-tree-dump-times "Empty EH handler" 1 "ehcleanup1" } }
> +// { dg-final { scan-tree-dump-times "Empty EH handler" 2 "ehcleanup1" } }
> //
> // And as a result also contained control flow.
> -// { dg-final { scan-tree-dump-times "Removing unreachable" 2 "ehcleanup1" }
> }
> +// { dg-final { scan-tree-dump-times "Removing unreachable" 4 "ehcleanup1" }
> }
> //
> // { dg-final { cleanup-tree-dump "ehcleanup1" } }
> Index: testsuite/g++.dg/eh/builtin1.C
> ===================================================================
> --- testsuite/g++.dg/eh/builtin1.C.orig 2011-11-07 15:56:25.000000000 +0100
> +++ testsuite/g++.dg/eh/builtin1.C 2011-11-07 16:12:35.000000000 +0100
> @@ -6,20 +6,26 @@
>
> extern "C" int printf (const char *, ...);
>
> -struct A { A (); ~A (); int i; };
> +extern void callme (void) throw();
>
> int
> -foo ()
> +foo (int i)
> {
> - A a;
> - printf ("foo %d\n", a.i);
> + try {
> + printf ("foo %d\n", i);
> + } catch (...) {
> + callme();
> + }
> }
>
> int
> -bar ()
> +bar (int i)
> {
> - A a;
> - __builtin_printf ("foo %d\n", a.i);
> + try {
> + __builtin_printf ("foo %d\n", i);
> + } catch (...) {
> + callme();
> + }
> }
>
> /* { dg-final { scan-tree-dump-times "resx" 2 "eh" } } */
> Index: testsuite/g++.dg/eh/builtin2.C
> ===================================================================
> --- testsuite/g++.dg/eh/builtin2.C.orig 2011-11-07 15:56:25.000000000 +0100
> +++ testsuite/g++.dg/eh/builtin2.C 2011-11-07 16:12:35.000000000 +0100
> @@ -5,20 +5,26 @@
>
> extern "C" int printf (const char *, ...) throw();
>
> -struct A { A (); ~A (); int i; };
> +extern void callme (void) throw();
>
> int
> -foo ()
> +foo (int i)
> {
> - A a;
> - printf ("foo %d\n", a.i);
> + try {
> + printf ("foo %d\n", i);
> + } catch (...) {
> + callme();
> + }
> }
>
> int
> -bar ()
> +bar (int i)
> {
> - A a;
> - __builtin_printf ("foo %d\n", a.i);
> + try {
> + __builtin_printf ("foo %d\n", i);
> + } catch (...) {
> + callme();
> + }
> }
>
> /* { dg-final { scan-tree-dump-times "resx" 0 "eh" } } */
> Index: testsuite/g++.dg/eh/builtin3.C
> ===================================================================
> --- testsuite/g++.dg/eh/builtin3.C.orig 2011-11-07 15:56:25.000000000 +0100
> +++ testsuite/g++.dg/eh/builtin3.C 2011-11-07 16:12:35.000000000 +0100
> @@ -3,13 +3,16 @@
> // { dg-do compile }
> // { dg-options "-fdump-tree-eh" }
>
> -struct A { A (); ~A (); int i; };
> +extern void callme (void) throw();
>
> int
> -bar ()
> +bar (int i)
> {
> - A a;
> - __builtin_printf ("foo %d\n", a.i);
> + try {
> + __builtin_printf ("foo %d\n", i);
> + } catch (...) {
> + callme();
> + }
> }
>
> /* { dg-final { scan-tree-dump-times "resx" 1 "eh" } } */
>