summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog39
-rw-r--r--gcc/cfgexpand.c206
-rw-r--r--gcc/gengtype.c3
-rw-r--r--gcc/gimple.c4
-rw-r--r--gcc/gimple.h8
-rw-r--r--gcc/gimplify.c39
-rw-r--r--gcc/testsuite/ChangeLog8
-rw-r--r--gcc/testsuite/g++.dg/eh/builtin1.C20
-rw-r--r--gcc/testsuite/g++.dg/eh/builtin2.C20
-rw-r--r--gcc/testsuite/g++.dg/eh/builtin3.C11
-rw-r--r--gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C4
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/20031015-1.c4
-rw-r--r--gcc/tree-pretty-print.c6
-rw-r--r--gcc/tree-sra.c4
-rw-r--r--gcc/tree-ssa-dce.c18
-rw-r--r--gcc/tree-ssa-live.c42
-rw-r--r--gcc/tree-ssa-operands.c6
-rw-r--r--gcc/tree-ssa-phiopt.c4
-rw-r--r--gcc/tree-ssa-sccvn.c6
-rw-r--r--gcc/tree-ssa-structalias.c6
-rw-r--r--gcc/tree-stdarg.c5
-rw-r--r--gcc/tree.h8
22 files changed, 406 insertions, 65 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 3062032ee46..abbc82c67e4 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,42 @@
+2011-11-08 Michael Matz <matz@suse.de>
+
+ * 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.
+
2011-11-08 Jakub Jelinek <jakub@redhat.com>
* config/i386/i386.c (ix86_preferred_simd_mode): Even for TARGET_AVX
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 3d733337cff..2a82b032f5d 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -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, bool really_expand)
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, bool toplevel)
|| !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. */
@@ -2008,8 +2163,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
{
@@ -3199,7 +3359,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;
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index c9c08a4db31..7450eebac4f 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -3651,14 +3651,13 @@ write_field_root (outf_p f, pair_p v, type_p type, const char *name,
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));
diff --git a/gcc/gimple.c b/gcc/gimple.c
index e803f56d0a3..57f15af313c 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -1499,7 +1499,9 @@ walk_gimple_op (gimple stmt, walk_tree_fn callback_op,
{
/* 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;
}
diff --git a/gcc/gimple.h b/gcc/gimple.h
index ffecc2617af..8536c70e87e 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -2053,6 +2053,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. */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 99e0d0dad90..9845b692211 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1135,7 +1135,8 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
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);
@@ -1181,22 +1182,50 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
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);
}
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index da0063417ac..0cc8b6e234d 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2011-11-08 Michael Matz <matz@suse.de>
+
+ * 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.
+
2011-11-08 Jakub Jelinek <jakub@redhat.com>
* gcc.target/i386/avx-cvt-2.c: Adjust for 32-byte integer vectors
diff --git a/gcc/testsuite/g++.dg/eh/builtin1.C b/gcc/testsuite/g++.dg/eh/builtin1.C
index ed49e9a962a..c0516eb6063 100644
--- a/gcc/testsuite/g++.dg/eh/builtin1.C
+++ b/gcc/testsuite/g++.dg/eh/builtin1.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/eh/builtin2.C b/gcc/testsuite/g++.dg/eh/builtin2.C
index fe0c4de6b3e..66104495289 100644
--- a/gcc/testsuite/g++.dg/eh/builtin2.C
+++ b/gcc/testsuite/g++.dg/eh/builtin2.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/eh/builtin3.C b/gcc/testsuite/g++.dg/eh/builtin3.C
index 45809b81553..4b382d98231 100644
--- a/gcc/testsuite/g++.dg/eh/builtin3.C
+++ b/gcc/testsuite/g++.dg/eh/builtin3.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C b/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C
index 8dfaa52b6bb..cc492a80975 100644
--- a/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C
+++ b/gcc/testsuite/g++.dg/tree-ssa/ehcleanup-1.C
@@ -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" } }
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/20031015-1.c b/gcc/testsuite/gcc.dg/tree-ssa/20031015-1.c
index 102715c0131..3cc2ae94687 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/20031015-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/20031015-1.c
@@ -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" } } */
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index d0546c6c1ca..9abe004280b 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -1271,8 +1271,10 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
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))
diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
index c83f480643c..472ad5d6f5d 100644
--- a/gcc/tree-sra.c
+++ b/gcc/tree-sra.c
@@ -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);
diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c
index 5a6a38e63e9..d6fbe622df0 100644
--- a/gcc/tree-ssa-dce.c
+++ b/gcc/tree-ssa-dce.c
@@ -351,6 +351,12 @@ mark_stmt_if_obviously_necessary (gimple stmt, bool aggressive)
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 *el)
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 *el)
/* 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 *el)
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);
}
diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
index 65b3855775a..2a2c13393fe 100644
--- a/gcc/tree-ssa-live.c
+++ b/gcc/tree-ssa-live.c
@@ -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. */
diff --git a/gcc/tree-ssa-operands.c b/gcc/tree-ssa-operands.c
index 9cd656b2a8c..9012bfe26fe 100644
--- a/gcc/tree-ssa-operands.c
+++ b/gcc/tree-ssa-operands.c
@@ -956,6 +956,12 @@ get_expr_operands (gimple stmt, tree *expr_p, int flags)
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++)
diff --git a/gcc/tree-ssa-phiopt.c b/gcc/tree-ssa-phiopt.c
index 42753447575..96d46122195 100644
--- a/gcc/tree-ssa-phiopt.c
+++ b/gcc/tree-ssa-phiopt.c
@@ -1318,8 +1318,10 @@ cond_if_else_store_replacement_1 (basic_block then_bb, basic_block else_bb,
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);
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index c33b87dd7fe..fa268c2ef8b 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -1388,8 +1388,12 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void *vr_)
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)
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index fe0c4e9c389..08d480a0d86 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -4484,7 +4484,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
{
diff --git a/gcc/tree-stdarg.c b/gcc/tree-stdarg.c
index 3e890163d01..ef898ee5bbd 100644
--- a/gcc/tree-stdarg.c
+++ b/gcc/tree-stdarg.c
@@ -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. */
diff --git a/gcc/tree.h b/gcc/tree.h
index 3e1e225bd4e..a76b0bc891a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1643,6 +1643,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