re PR tree-optimization/16867 (Inline array initializer miscompilation at -O)
authorDiego Novillo <dnovillo@redhat.com>
Thu, 12 Aug 2004 14:34:11 +0000 (14:34 +0000)
committerDiego Novillo <dnovillo@gcc.gnu.org>
Thu, 12 Aug 2004 14:34:11 +0000 (10:34 -0400)
PR tree-optimization/16867
* tree.c (is_global_var): New function.
(needs_to_live_in_memory): Check for TREE_ADDRESSABLE.
Call is_global_var.
* tree.h (DECL_NEEDS_TO_LIVE_IN_MEMORY_INTERNAL): Remove.
Update all users.
(is_global_var): Declare.
* tree-dfa.c (dump_variable): Display global and addressable
attributes.
(add_referenced_var): Clarify documentation when marking
variables call-clobbered.
* tree-flow-inline.h (is_call_clobbered): Call is_global_var
instead of needs_to_live_in_memory.
(mark_call_clobbered): If the variable is a tag, mark it
DECL_EXTERNAL.
* tree-gimple.c (is_gimple_reg): Don't check for
TREE_ADDRESSABLE.
(is_gimple_non_addressable): Likewise.
* tree-ssa-alias.c (get_nmt_for): Always check whether the tag
needs to be marked call-clobbered.
(setup_pointers_and_addressables): Call is_global_var instead
of needs_to_live_in_memory.
* tree-ssa-dce.c (need_to_preserve_store): Remove.
Update all users with is_global_var.
(mark_stmt_if_obviously_necessary): Fix processing of aliased
stores.  Don't check the virtual definitions.  Rather, check
whether the store is going into global memory.
(find_obviously_necessary_stmts): Get the symbol from the PHI
result.
* tree-ssa-operands.c (get_call_expr_operands): Do not add
clobbering may-defs if the call does not have side effects.

libjava/ChangeLog

PR tree-optimization/16867
* testsuite/libjava.lang/PR16867.java: New test.

From-SVN: r85874

12 files changed:
gcc/ChangeLog
gcc/tree-dfa.c
gcc/tree-flow-inline.h
gcc/tree-gimple.c
gcc/tree-ssa-alias.c
gcc/tree-ssa-dce.c
gcc/tree-ssa-operands.c
gcc/tree.c
gcc/tree.h
libjava/ChangeLog
libjava/testsuite/libjava.lang/PR16867.java [new file with mode: 0644]
libjava/testsuite/libjava.lang/PR16867.out [new file with mode: 0644]

index 4331d9a..b9c2ff0 100644 (file)
@@ -1,3 +1,37 @@
+2004-08-12  Diego Novillo  <dnovillo@redhat.com>
+
+       PR tree-optimization/16867
+       * tree.c (is_global_var): New function.
+       (needs_to_live_in_memory): Check for TREE_ADDRESSABLE.
+       Call is_global_var.
+       * tree.h (DECL_NEEDS_TO_LIVE_IN_MEMORY_INTERNAL): Remove.
+       Update all users.
+       (is_global_var): Declare.
+       * tree-dfa.c (dump_variable): Display global and addressable
+       attributes.
+       (add_referenced_var): Clarify documentation when marking
+       variables call-clobbered.
+       * tree-flow-inline.h (is_call_clobbered): Call is_global_var
+       instead of needs_to_live_in_memory.
+       (mark_call_clobbered): If the variable is a tag, mark it
+       DECL_EXTERNAL.
+       * tree-gimple.c (is_gimple_reg): Don't check for
+       TREE_ADDRESSABLE.
+       (is_gimple_non_addressable): Likewise.
+       * tree-ssa-alias.c (get_nmt_for): Always check whether the tag
+       needs to be marked call-clobbered.
+       (setup_pointers_and_addressables): Call is_global_var instead
+       of needs_to_live_in_memory.
+       * tree-ssa-dce.c (need_to_preserve_store): Remove.
+       Update all users with is_global_var.
+       (mark_stmt_if_obviously_necessary): Fix processing of aliased
+       stores.  Don't check the virtual definitions.  Rather, check
+       whether the store is going into global memory.
+       (find_obviously_necessary_stmts): Get the symbol from the PHI
+       result.
+       * tree-ssa-operands.c (get_call_expr_operands): Do not add
+       clobbering may-defs if the call does not have side effects.
+
 2004-08-12  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/16276
index 9e9ee32..0273406 100644 (file)
@@ -562,8 +562,11 @@ dump_variable (FILE *file, tree var)
   if (ann->is_alias_tag)
     fprintf (file, ", is an alias tag");
 
-  if (needs_to_live_in_memory (var))
-    fprintf (file, ", is %s", TREE_STATIC (var) ? "static" : "global");
+  if (TREE_ADDRESSABLE (var))
+    fprintf (file, ", is addressable");
+  
+  if (is_global_var (var))
+    fprintf (file, ", is global");
 
   if (is_call_clobbered (var))
     fprintf (file, ", call clobbered");
@@ -900,7 +903,9 @@ add_referenced_var (tree var, struct walk_state *walk_state)
       v_ann->uid = num_referenced_vars;
       VARRAY_PUSH_TREE (referenced_vars, var);
 
-      /* Global and static variables are call-clobbered, always.  */
+      /* Initially assume that all memory variables are
+        call-clobbered.  This will be refined later by the alias
+        analyzer.  */
       if (needs_to_live_in_memory (var))
        mark_call_clobbered (var);
 
index d3e0a32..a9cc213 100644 (file)
@@ -630,7 +630,7 @@ loop_containing_stmt (tree stmt)
 static inline bool
 is_call_clobbered (tree var)
 {
-  return needs_to_live_in_memory (var)
+  return is_global_var (var)
         || bitmap_bit_p (call_clobbered_vars, var_ann (var)->uid);
 }
 
@@ -639,8 +639,12 @@ static inline void
 mark_call_clobbered (tree var)
 {
   var_ann_t ann = var_ann (var);
-  /* Call-clobbered variables need to live in memory.  */
-  DECL_NEEDS_TO_LIVE_IN_MEMORY_INTERNAL (var) = 1;
+  /* If VAR is a memory tag, then we need to consider it a global
+     variable.  This is because the pointer that VAR represents has
+     been found to point to either an arbitrary location or to a known
+     location in global memory.  */
+  if (ann->mem_tag_kind != NOT_A_TAG)
+    DECL_EXTERNAL (var) = 1;
   bitmap_set_bit (call_clobbered_vars, ann->uid);
 }
 
@@ -649,7 +653,6 @@ static inline void
 mark_non_addressable (tree var)
 {
   bitmap_clear_bit (call_clobbered_vars, var_ann (var)->uid);
-  DECL_NEEDS_TO_LIVE_IN_MEMORY_INTERNAL (var) = 0;
   TREE_ADDRESSABLE (var) = 0;
 }
 
index 413395a..e208893 100644 (file)
@@ -441,7 +441,6 @@ is_gimple_reg (tree t)
          /* A volatile decl is not acceptable because we can't reuse it as
             needed.  We need to copy it into a temp first.  */
          && ! TREE_THIS_VOLATILE (t)
-         && ! TREE_ADDRESSABLE (t)
          && ! needs_to_live_in_memory (t));
 }
 
@@ -481,9 +480,7 @@ is_gimple_non_addressable (tree t)
   if (TREE_CODE (t) == SSA_NAME)
     t = SSA_NAME_VAR (t);
 
-  return (is_gimple_variable (t)
-         && ! TREE_ADDRESSABLE (t)
-         && ! needs_to_live_in_memory (t));
+  return (is_gimple_variable (t) && ! needs_to_live_in_memory (t));
 }
 
 /* Return true if T is a GIMPLE rvalue, i.e. an identifier or a constant.  */
index 1ec2464..a344384 100644 (file)
@@ -390,7 +390,6 @@ init_alias_info (void)
       EXECUTE_IF_SET_IN_BITMAP (call_clobbered_vars, 0, i,
        {
          tree var = referenced_var (i);
-         DECL_NEEDS_TO_LIVE_IN_MEMORY_INTERNAL (var) = 0;
 
          /* Variables that are intrinsically call-clobbered (globals,
             local statics, etc) will not be marked by the aliasing
@@ -1329,7 +1328,7 @@ setup_pointers_and_addressables (struct alias_info *ai)
        {
          if (!bitmap_bit_p (ai->addresses_needed, v_ann->uid)
              && v_ann->mem_tag_kind == NOT_A_TAG
-             && !needs_to_live_in_memory (var))
+             && !is_global_var (var))
            {
              /* The address of VAR is not needed, remove the
                 addressable bit, so that it can be optimized as a
@@ -1391,7 +1390,7 @@ setup_pointers_and_addressables (struct alias_info *ai)
              /* If pointer VAR is a global variable or a PARM_DECL,
                 then its memory tag should be considered a global
                 variable.  */
-             if (TREE_CODE (var) == PARM_DECL || needs_to_live_in_memory (var))
+             if (TREE_CODE (var) == PARM_DECL || is_global_var (var))
                mark_call_clobbered (tag);
 
              /* All the dereferences of pointer VAR count as
@@ -2105,18 +2104,16 @@ get_nmt_for (tree ptr)
   tree tag = pi->name_mem_tag;
 
   if (tag == NULL_TREE)
-    {
-      tag = create_memory_tag (TREE_TYPE (TREE_TYPE (ptr)), false);
+    tag = create_memory_tag (TREE_TYPE (TREE_TYPE (ptr)), false);
 
-      /* If PTR is a PARM_DECL, its memory tag should be considered a
-        global variable.  */
-      if (TREE_CODE (SSA_NAME_VAR (ptr)) == PARM_DECL)
-       mark_call_clobbered (tag);
+  /* If PTR is a PARM_DECL, its memory tag should be considered a global
+     variable.  */
+  if (TREE_CODE (SSA_NAME_VAR (ptr)) == PARM_DECL)
+    mark_call_clobbered (tag);
 
-      /* Similarly, if PTR points to malloc, then TAG is a global.  */
-      if (pi->pt_malloc)
-       mark_call_clobbered (tag);
-    }
+  /* Similarly, if PTR points to malloc, then TAG is a global.  */
+  if (pi->pt_malloc)
+    mark_call_clobbered (tag);
 
   return tag;
 }
index c94a2f6..111f540 100644 (file)
@@ -107,7 +107,6 @@ static inline basic_block find_pdom (basic_block);
 static inline void mark_stmt_necessary (tree, bool);
 static inline void mark_operand_necessary (tree);
 
-static bool need_to_preserve_store (tree);
 static void mark_stmt_if_obviously_necessary (tree, bool);
 static void find_obviously_necessary_stmts (struct edge_list *);
 
@@ -267,14 +266,6 @@ mark_operand_necessary (tree op)
   VARRAY_PUSH_TREE (worklist, stmt);
 }
 \f
-/* Return true if a store to a variable needs to be preserved.  */
-
-static inline bool
-need_to_preserve_store (tree ssa_name)
-{
-  return (needs_to_live_in_memory (SSA_NAME_VAR (ssa_name)));
-}
-\f
 
 /* Mark STMT as necessary if it is obviously is.  Add it to the worklist if
    it can make other statements necessary.
@@ -367,10 +358,11 @@ mark_stmt_if_obviously_necessary (tree stmt, bool aggressive)
     }
 
   ann = stmt_ann (stmt);
-  /* If the statement has volatile operands, it needs to be preserved.  Same
-     for statements that can alter control flow in unpredictable ways.  */
-  if (ann->has_volatile_ops
-      || is_ctrl_altering_stmt (stmt))
+
+  /* If the statement has volatile operands, it needs to be preserved.
+     Same for statements that can alter control flow in unpredictable
+     ways.  */
+  if (ann->has_volatile_ops || is_ctrl_altering_stmt (stmt))
     {
       mark_stmt_necessary (stmt, true);
       return;
@@ -382,33 +374,87 @@ mark_stmt_if_obviously_necessary (tree stmt, bool aggressive)
   for (i = 0; i < NUM_DEFS (defs); i++)
     {
       tree def = DEF_OP (defs, i);
-      if (need_to_preserve_store (def))
+      if (is_global_var (SSA_NAME_VAR (def)))
        {
          mark_stmt_necessary (stmt, true);
          return;
         }
     }
 
+  /* Check virtual definitions.  If we get here, the only virtual
+     definitions we should see are those generated by assignment
+     statements.  */
   v_may_defs = V_MAY_DEF_OPS (ann);
-  for (i = 0; i < NUM_V_MAY_DEFS (v_may_defs); i++)
-    {
-      tree v_may_def = V_MAY_DEF_RESULT (v_may_defs, i);
-      if (need_to_preserve_store (v_may_def))
-       {
-         mark_stmt_necessary (stmt, true);
-         return;
-        }
-    }
-    
   v_must_defs = V_MUST_DEF_OPS (ann);
-  for (i = 0; i < NUM_V_MUST_DEFS (v_must_defs); i++)
+  if (NUM_V_MAY_DEFS (v_may_defs) > 0 || NUM_V_MUST_DEFS (v_must_defs) > 0)
     {
-      tree v_must_def = V_MUST_DEF_OP (v_must_defs, i);
-      if (need_to_preserve_store (v_must_def))
+      tree lhs;
+
+#if defined ENABLE_CHECKING
+      if (TREE_CODE (stmt) != MODIFY_EXPR)
+       abort ();
+#endif
+
+      /* Note that we must not check the individual virtual operands
+        here.  In particular, if this is an aliased store, we could
+        end up with something like the following (SSA notation
+        redacted for brevity):
+
+               foo (int *p, int i)
+               {
+                 int x;
+                 p_1 = (i_2 > 3) ? &x : p_1;
+
+                 # x_4 = V_MAY_DEF <x_3>
+                 *p_1 = 5;
+
+                 return 2;
+               }
+
+        Notice that the store to '*p_1' should be preserved, if we
+        were to check the virtual definitions in that store, we would
+        not mark it needed.  This is because 'x' is not a global
+        variable.
+
+        Therefore, we check the base address of the LHS.  If the
+        address is a pointer, we check if its name tag or type tag is
+        a global variable.  Otherwise, we check if the base variable
+        is a global.  */
+      lhs = TREE_OPERAND (stmt, 0);
+      if (TREE_CODE_CLASS (TREE_CODE (lhs)) == 'r')
+       lhs = get_base_address (lhs);
+
+      if (lhs == NULL_TREE)
        {
+         /* If LHS is NULL, it means that we couldn't get the base
+            address of the reference.  In which case, we should not
+            remove this store. */
          mark_stmt_necessary (stmt, true);
-         return;
-        }
+       }
+      else if (DECL_P (lhs))
+       {
+         /* If the store is to a global symbol, we need to keep it.  */
+         if (is_global_var (lhs))
+           mark_stmt_necessary (stmt, true);
+       }
+      else if (TREE_CODE (lhs) == INDIRECT_REF)
+       {
+         tree ptr = TREE_OPERAND (lhs, 0);
+         struct ptr_info_def *pi = SSA_NAME_PTR_INFO (ptr);
+         tree nmt = (pi) ? pi->name_mem_tag : NULL_TREE;
+         tree tmt = var_ann (SSA_NAME_VAR (ptr))->type_mem_tag;
+
+         /* If either the name tag or the type tag for PTR is a
+            global variable, then the store is necessary.  */
+         if ((nmt && is_global_var (nmt))
+             || (tmt && is_global_var (tmt)))
+           {
+             mark_stmt_necessary (stmt, true);
+             return;
+           }
+       }
+      else
+       abort ();
     }
 
   return;
@@ -444,7 +490,7 @@ find_obviously_necessary_stmts (struct edge_list *el)
             Thus, we only need to mark PHIs for real variables which
             need their result preserved as being inherently necessary.  */
          if (is_gimple_reg (PHI_RESULT (phi))
-             && need_to_preserve_store (PHI_RESULT (phi)))
+             && is_global_var (SSA_NAME_VAR (PHI_RESULT (phi))))
            mark_stmt_necessary (phi, true);
         }
 
index fe291d0..4b899ee 100644 (file)
@@ -1373,8 +1373,8 @@ get_call_expr_operands (tree stmt, tree expr)
       /* A 'pure' or a 'const' functions never call clobber anything. 
         A 'noreturn' function might, but since we don't return anyway 
         there is no point in recording that.  */ 
-      if (!(call_flags
-           & (ECF_PURE | ECF_CONST | ECF_NORETURN)))
+      if (TREE_SIDE_EFFECTS (expr)
+         && !(call_flags & (ECF_PURE | ECF_CONST | ECF_NORETURN)))
        add_call_clobber_ops (stmt);
       else if (!(call_flags & (ECF_CONST | ECF_NORETURN)))
        add_call_read_ops (stmt);
index c8153b8..a6b17da 100644 (file)
@@ -5624,15 +5624,22 @@ in_array_bounds_p (tree ref)
   return true;
 }
 
+/* Return true if T (assumed to be a DECL) is a global variable.  */
+
+bool
+is_global_var (tree t)
+{
+  return (TREE_STATIC (t) || DECL_EXTERNAL (t));
+}
+
 /* Return true if T (assumed to be a DECL) must be assigned a memory
    location.  */
 
 bool
 needs_to_live_in_memory (tree t)
 {
-  return (DECL_NEEDS_TO_LIVE_IN_MEMORY_INTERNAL (t)
-         || TREE_STATIC (t)
-          || DECL_EXTERNAL (t)
+  return (TREE_ADDRESSABLE (t)
+         || is_global_var (t)
          || (TREE_CODE (t) == RESULT_DECL
              && aggregate_value_p (t, current_function_decl)));
 }
index 0d6d560..e596f90 100644 (file)
@@ -2161,15 +2161,6 @@ struct tree_binfo GTY (())
   (! DECL_CONTEXT (EXP)                                                \
    || TREE_CODE (DECL_CONTEXT (EXP)) == TRANSLATION_UNIT_DECL)
 
-/* Nonzero for a decl that has been marked as needing a memory slot.
-   NOTE: Never use this macro directly.  It will give you incomplete
-   information. Most of the time this bit will only be set after alias
-   analysis in the tree optimizers.  It's always better to call
-   needs_to_live_in_memory instead.  To mark memory variables use
-   mark_call_clobbered.  */
-#define DECL_NEEDS_TO_LIVE_IN_MEMORY_INTERNAL(DECL)            \
-  DECL_CHECK (DECL)->decl.needs_to_live_in_memory
-
 /* Nonzero for a decl that cgraph has decided should be inlined into
    at least one call site.  It is not meaningful to look at this
    directly; always use cgraph_function_possibly_inlined_p.  */
@@ -2242,9 +2233,8 @@ struct tree_decl GTY(())
   unsigned lang_flag_6 : 1;
   unsigned lang_flag_7 : 1;
 
-  unsigned needs_to_live_in_memory : 1;
   unsigned possibly_inlined : 1;
-  /* 14 unused bits.  */
+  /* 15 unused bits.  */
 
   union tree_decl_u1 {
     /* In a FUNCTION_DECL for which DECL_BUILT_IN holds, this is
@@ -3479,6 +3469,7 @@ extern void expand_function_end (void);
 extern void expand_function_start (tree);
 extern void expand_pending_sizes (tree);
 extern void recompute_tree_invarant_for_addr_expr (tree);
+extern bool is_global_var (tree t);
 extern bool needs_to_live_in_memory (tree);
 extern tree reconstruct_complex_type (tree, tree);
 
index 1d2af7a..54e307c 100644 (file)
@@ -1,3 +1,8 @@
+2004-08-12  Diego Novillo  <dnovillo@redhat.com>
+
+       PR tree-optimization/16867
+       * testsuite/libjava.lang/PR16867.java: New test.
+
 2004-08-09  Per Bothner  <per@bothner.com>
 
        * gcj/javaprims.h (_Jv_Utf8Const): Change struct to a class,
diff --git a/libjava/testsuite/libjava.lang/PR16867.java b/libjava/testsuite/libjava.lang/PR16867.java
new file mode 100644 (file)
index 0000000..6862892
--- /dev/null
@@ -0,0 +1,17 @@
+/* SSA-DCE was removing the initialization of the temporary object
+   in getFoo because it wasn't realizing that the pointer was needed
+   outside of it.  */
+
+public class PR16867
+{
+  public static Object[] getFoo()
+  {
+    return new Object[] {"OK"};
+  }
+
+  public static void main(String[] args)
+  {
+    Object[] a = getFoo();
+    System.out.println(a[0]);
+  }
+}
diff --git a/libjava/testsuite/libjava.lang/PR16867.out b/libjava/testsuite/libjava.lang/PR16867.out
new file mode 100644 (file)
index 0000000..d86bac9
--- /dev/null
@@ -0,0 +1 @@
+OK