This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Patch for PR39557


Hi,  please find the attached patch for fixing PR39557.

Some explanations:

There are three parts of the patch:

1) tree-ssa.c change.

In fact, this is the only change that is needed to fix the problem.

2) dominance.c
     params.def

As suggested by Diego, I added the check. It was quite painful to
triage the runtime problem in a very large multithreaded server
program, so anything that helps converted to compile time is good.
Diego suggested moving the check to verify_interpass_invariants -- but
this doe not work during the lowering passes when the cfg is not even
created (of course I can add the check for that).  Doing the check on
the fly when a pass need the dom info seems ok to me.

The check is guarded under a parameter because more test failures will
occur when it is turned on by default -- bugs will be filed later when
this patch is checked in.

3) Ira related changes -- strictly not necessary for this -- but the
problem was detected with the dom-validation check and it is easy to
fix.

About the fix itself:

Independent of the invalid PDOM bug,  the way tree-ssa-dce's cfg
patchup when removing control statement looks suspicious (
remove_dead_stmt):

      if (! post_dom_bb
	  || post_dom_bb == EXIT_BLOCK_PTR
	  || phi_nodes (post_dom_bb))                 <------          ????
What is this?
	;
      else
	{
	  /* Redirect the first edge out of BB to reach POST_DOM_BB.  */
	  redirect_edge_and_branch (EDGE_SUCC (bb, 0), post_dom_bb);
	  PENDING_STMT (EDGE_SUCC (bb, 0)) = NULL;

	  /* It is not sufficient to set cfg_altered below during edge
	     removal, in case BB has two successors and one of them
	     is POST_DOM_BB.  */
	  cfg_altered = true;
	}
      EDGE_SUCC (bb, 0)->probability = REG_BR_PROB_BASE;
      EDGE_SUCC (bb, 0)->count = bb->count;

      /* The edge is no longer associated with a conditional, so it does
	 not have TRUE/FALSE flags.  */
      EDGE_SUCC (bb, 0)->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE);

      /* The lone outgoing edge from BB will be a fallthru edge.  */
      EDGE_SUCC (bb, 0)->flags |= EDGE_FALLTHRU;

      /* Remove the remaining the outgoing edges.  */
      while (!single_succ_p (bb))
            <----------------------   doing lottery if reaching from
the 'if' statement above ??
	{
	  /* FIXME.  When we remove the edge, we modify the CFG, which
	     in turn modifies the dominator and post-dominator tree.
	     Is it safe to postpone recomputing the dominator and
	     post-dominator tree until the end of this pass given that
	     the post-dominators are used above?  */
	  cfg_altered = true;
          remove_edge (EDGE_SUCC (bb, 1));
	}
    }

The code marked with '<---" are direct cause (not the root one) that
lead to the inifinte loop.  Of course with the right PDOM info and the
way loop is handled, the code won't be excersised,  so  I will leave
it alone.


By the way, is there a plan to make DOM/PDOM incremental update more robust?

Thanks,

David
Index: tree-ssa.c
===================================================================
--- tree-ssa.c	(revision 145035)
+++ tree-ssa.c	(working copy)
@@ -1589,6 +1589,11 @@ warn_uninitialized_vars (bool warn_possi
 	  walk_gimple_op (gsi_stmt (gsi), warn_uninitialized_var, &wi);
 	}
     }
+
+  /* Post-dominator information can not be reliably updated. Free it
+     after the use.  */
+
+  free_dominance_info (CDI_POST_DOMINATORS);
   return 0;
 }
 
Index: dominance.c
===================================================================
--- dominance.c	(revision 145035)
+++ dominance.c	(working copy)
@@ -47,6 +47,7 @@
 #include "vecprim.h"
 #include "pointer-set.h"
 #include "graphds.h"
+#include "params.h"
 
 /* We name our nodes with integers, beginning with 1.  Zero is reserved for
    'undefined' or 'end of list'.  The name of each node is given by the dfs
@@ -666,6 +667,10 @@ calculate_dominance_info (enum cdi_direc
       free_dom_info (&di);
       dom_computed[dir_index] = DOM_NO_FAST_QUERY;
     }
+#ifdef ENABLE_CHECKING
+  else if (PARAM_VALUE (PARAM_CHECK_DOM))
+    verify_dominators (dir);
+#endif
 
   compute_dom_fast_query (dir);
 
Index: params.def
===================================================================
--- params.def	(revision 145035)
+++ params.def	(working copy)
@@ -771,6 +771,13 @@ DEFPARAM (PARAM_LOOP_INVARIANT_MAX_BBS_I
 	  "max basic blocks number in loop for loop invariant motion",
 	  10000, 0, 0)
 
+/* Enable dominance information validation.  */
+
+DEFPARAM (PARAM_CHECK_DOM,
+	  "verify-dom",
+	  "enable verficiation of dominance information",
+	  0, 0, 1)
+
 /*
 Local variables:
 mode:c
Index: ira.c
===================================================================
--- ira.c	(revision 145035)
+++ ira.c	(working copy)
@@ -3055,6 +3055,7 @@ ira (FILE *f)
   int rebuild_p;
   int saved_flag_ira_share_spill_slots;
   basic_block bb;
+  bool cfg_changed = false;
 
   timevar_push (TV_IRA);
 
@@ -3153,7 +3154,7 @@ ira (FILE *f)
       
   ira_max_point_before_emit = ira_max_point;
       
-  ira_emit (loops_p);
+  cfg_changed = ira_emit (loops_p);
   
   if (ira_conflicts_p)
     {
@@ -3175,6 +3176,9 @@ ira (FILE *f)
 	     info.  */
 	  df_analyze ();
 	  
+          if (cfg_changed)
+            /* Edge split in ira_emit might have invalidated the dom info.  */
+            free_dominance_info (CDI_DOMINATORS);
 	  flow_loops_find (&ira_loops);
 	  current_loops = &ira_loops;
 
Index: ira-emit.c
===================================================================
--- ira-emit.c	(revision 145035)
+++ ira-emit.c	(working copy)
@@ -1047,8 +1047,8 @@ add_ranges_and_copies (void)
 
 /* The entry function changes code and generates shuffling allocnos on
    region borders for the regional (LOOPS_P is TRUE in this case)
-   register allocation.  */
-void
+   register allocation.  Returns true of any changes are made in cfg.   */
+bool
 ira_emit (bool loops_p)
 {
   basic_block bb;
@@ -1057,11 +1057,12 @@ ira_emit (bool loops_p)
   edge e;
   ira_allocno_t a;
   ira_allocno_iterator ai;
+  bool cfg_changed = false;
 
   FOR_EACH_ALLOCNO (a, ai)
     ALLOCNO_REG (a) = regno_reg_rtx[ALLOCNO_REGNO (a)];
   if (! loops_p)
-    return;
+    return false;
   at_bb_start = (move_t *) ira_allocate (sizeof (move_t) * last_basic_block);
   memset (at_bb_start, 0, sizeof (move_t) * last_basic_block);
   at_bb_end = (move_t *) ira_allocate (sizeof (move_t) * last_basic_block);
@@ -1112,7 +1113,7 @@ ira_emit (bool loops_p)
   VEC_free (move_t, heap, move_vec);
   ira_free (allocno_last_set_check);
   ira_free (allocno_last_set);
-  commit_edge_insertions ();
+  cfg_changed = commit_edge_insertions ();
   /* Fix insn codes.  It is necessary to do it before reload because
      reload assumes initial insn codes defined.  The insn codes can be
      invalidated by CFG infrastructure for example in jump
@@ -1123,4 +1124,5 @@ ira_emit (bool loops_p)
 	recog_memoized (insn);
   ira_free (at_bb_end);
   ira_free (at_bb_start);
+  return cfg_changed;
 }
Index: cfgrtl.c
===================================================================
--- cfgrtl.c	(revision 145035)
+++ cfgrtl.c	(working copy)
@@ -1459,9 +1459,10 @@ commit_one_edge_insertion (edge e)
     bb->aux = &bb->aux;
 }
 
-/* Update the CFG for all queued instructions.  */
+/* Update the CFG for all queued instructions.  Returns true
+   if there is any change.  */
 
-void
+bool
 commit_edge_insertions (void)
 {
   basic_block bb;
@@ -1486,14 +1487,14 @@ commit_edge_insertions (void)
     }
 
   if (!changed)
-    return;
+    return false;
 
   /* In the old rtl CFG API, it was OK to insert control flow on an
      edge, apparently?  In cfglayout mode, this will *not* work, and
      the caller is responsible for making sure that control flow is
      valid at all times.  */
   if (current_ir_type () == IR_RTL_CFGLAYOUT)
-    return;
+    return true;
 
   blocks = sbitmap_alloc (last_basic_block);
   sbitmap_zero (blocks);
@@ -1508,6 +1509,7 @@ commit_edge_insertions (void)
       }
   find_many_sub_basic_blocks (blocks);
   sbitmap_free (blocks);
+  return true;
 }
 
 
Index: ira-int.h
===================================================================
--- ira-int.h	(revision 145035)
+++ ira-int.h	(working copy)
@@ -937,7 +937,7 @@ extern void ira_finish_assign (void);
 extern void ira_color (void);
 
 /* ira-emit.c */
-extern void ira_emit (bool);
+extern bool ira_emit (bool);
 
 
 
Index: basic-block.h
===================================================================
--- basic-block.h	(revision 145035)
+++ basic-block.h	(working copy)
@@ -512,7 +512,7 @@ extern void update_bb_for_insn (basic_bl
 extern void insert_insn_on_edge (rtx, edge);
 basic_block split_edge_and_insert (edge, rtx);
 
-extern void commit_edge_insertions (void);
+extern bool commit_edge_insertions (void);
 
 extern void remove_fake_edges (void);
 extern void remove_fake_exit_edges (void);
Index: testsuite/g++.dg/tree-ssa/dom-invalid.C
===================================================================
--- testsuite/g++.dg/tree-ssa/dom-invalid.C	(revision 0)
+++ testsuite/g++.dg/tree-ssa/dom-invalid.C	(revision 0)
@@ -0,0 +1,147 @@
+// PR xxxx : invalid post-dom info leads to infinite loop
+// { dg-do run }
+/* { dg-options " -Wall -fno-exceptions -O2 -fprofile-use=/blah  -fno-rtti " } */
+
+extern "C" {
+ typedef long unsigned int size_t;
+ }
+                  namespace std __attribute__ ((__visibility__ ("default"))) {
+ }
+                  typedef long int ptrdiff_t;
+  namespace std __attribute__ ((__visibility__ ("default"))) {
+   using ::size_t;
+   void   __throw_bad_alloc(void) __attribute__((__noreturn__));
+ }
+          namespace __gnu_cxx __attribute__ ((__visibility__ ("default"))) {
+ }
+  namespace std __attribute__ ((__visibility__ ("default"))) {
+ }
+          namespace __gnu_cxx __attribute__ ((__visibility__ ("default"))) {
+ }
+  namespace std __attribute__ ((__visibility__ ("default"))) {
+   struct input_iterator_tag { };
+   struct forward_iterator_tag : public input_iterator_tag { };
+   struct bidirectional_iterator_tag : public forward_iterator_tag { };
+   struct random_access_iterator_tag : public bidirectional_iterator_tag { };
+   template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t,            typename _Pointer = _Tp*, typename _Reference = _Tp&>     struct iterator     {       typedef _Category iterator_category;       typedef _Tp value_type;       typedef _Distance difference_type;       typedef _Pointer pointer;       typedef _Reference reference;     };
+ }
+ typedef struct {
+ }  __mbstate_t;
+ typedef __mbstate_t mbstate_t;
+  namespace std __attribute__ ((__visibility__ ("default"))) {
+   using ::mbstate_t;
+ }
+  namespace std __attribute__ ((__visibility__ ("default"))) {
+   typedef long streamoff;
+   template<typename _StateT>     class fpos     {     private:       streamoff _M_off;       _StateT _M_state;     public:       fpos()       : _M_off(0), _M_state() { }       fpos(streamoff __off)       : _M_off(__off), _M_state() { }       operator streamoff() const { return _M_off; }       void       state(_StateT __st)       { _M_state = __st; }       _StateT       state() const       { return _M_state; }       fpos&       operator+=(streamoff __off)       {  _M_off += __off;  fpos __pos(*this);  __pos += __off;  return __pos;       }       fpos       operator-(streamoff __off) const       {  fpos __pos(*this);  __pos -= __off;  return __pos;       }       streamoff       operator-(const fpos& __other) const       { return _M_off - __other._M_off; }     };
+   typedef fpos<mbstate_t> streampos;
+ }
+                  namespace __gnu_cxx __attribute__ ((__visibility__ ("default"))) {
+   template<typename _CharT>     struct _Char_types     {       typedef unsigned long int_type;       typedef std::streampos pos_type;       typedef std::streamoff off_type;       typedef std::mbstate_t state_type;     };
+   template<typename _CharT>     struct char_traits     {       typedef _CharT char_type;       typedef typename _Char_types<_CharT>::int_type int_type;       typedef typename _Char_types<_CharT>::pos_type pos_type;       typedef typename _Char_types<_CharT>::off_type off_type;       typedef typename _Char_types<_CharT>::state_type state_type;       static void       assign(char_type& __c1, const char_type& __c2)       { __c1 = __c2; }       static bool       eq(const char_type& __c1, const char_type& __c2)       { return __c1 < __c2; }       static int       compare(const char_type* __s1, const char_type* __s2, std::size_t __n);       static std::size_t       length(const char_type* __s);       static const char_type*       find(const char_type* __s, std::size_t __n, const char_type& __a);       static char_type*       move(char_type* __s1, const char_type* __s2, std::size_t __n);       static char_type*       copy(char_type* __s1, const char_type* __s2, std::size_t __n);       static char_type*       assign(char_type* __s, std::size_t __n, char_type __a);       static char_type       to_char_type(const int_type& __c)       { return static_cast<char_type>(__c); }       static int_type       to_int_type(const char_type& __c)       { return static_cast<int_type>(__c); }       static bool       eq_int_type(const int_type& __c1, const int_type& __c2)       { return __c1 == __c2; }       static int_type       eof()       { return static_cast<int_type>((-1)); }       static int_type       not_eof(const int_type& __c)       { return !eq_int_type(__c, eof()) ? __c : to_int_type(char_type()); }     };
+ }
+  namespace std __attribute__ ((__visibility__ ("default"))) {
+   template<class _CharT>     struct char_traits : public __gnu_cxx::char_traits<_CharT>     { };
+ }
+  namespace __gnu_cxx __attribute__ ((__visibility__ ("default"))) {
+   template<typename _Tp>     class new_allocator     {     public:       typedef size_t size_type;       typedef ptrdiff_t difference_type;       typedef _Tp* pointer;       typedef const _Tp* const_pointer;       typedef _Tp& reference;       typedef const _Tp& const_reference;       ~new_allocator() throw() { }       pointer       address(reference __x) const { return &__x; }       const_pointer       address(const_reference __x) const { return &__x; }       pointer       allocate(size_type __n, const void* = 0)       {  if (__builtin_expect(__n > this->max_size(), false))    std::__throw_bad_alloc();  return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));       }       void       deallocate(pointer __p, size_type)       { ::operator delete(__p); }       size_type       max_size() const throw()       { return size_t(-1) / sizeof(_Tp); }       void       construct(pointer __p, const _Tp& __val)       { ::new((void *)__p) _Tp(__val); }       void       destroy(pointer __p) { __p->~_Tp(); }     };
+ }
+  namespace std __attribute__ ((__visibility__ ("default"))) {
+   template<typename _Tp>     class allocator: public __gnu_cxx::new_allocator<_Tp>     {    public:       typedef size_t size_type;       typedef const _Tp& const_reference;       typedef _Tp value_type;       template<typename _Tp1>         struct rebind         { typedef allocator<_Tp1> other; };       allocator() throw() { }       allocator(const allocator& __a) throw()       : __gnu_cxx::new_allocator<_Tp>(__a) { }     };
+ }
+  __extension__ typedef struct {
+ }
+  __fsid_t;
+  namespace std __attribute__ ((__visibility__ ("default"))) {
+ }
+  typedef union {
+   struct   {     int __lock;     int __spins;   }
+ __data;
+ }
+  pthread_mutexattr_t;
+  typedef union {
+ }
+  pthread_condattr_t;
+  typedef union {
+   struct   {     int __lock;     unsigned int __nr_readers;     unsigned long int __pad3;     unsigned int __flags;   }
+ __data;
+ }
+  pthread_barrierattr_t;
+  namespace __gnu_cxx __attribute__ ((__visibility__ ("default"))) {
+   template<typename _CharT, typename _Traits, typename _Alloc>     class __sso_string_base;
+   template<typename _CharT, typename _Traits = std::char_traits<_CharT>,            typename _Alloc = std::allocator<_CharT>,     template     <typename, typename, typename> class _Base = __sso_string_base>     class __versa_string;
+ }
+  namespace __gnu_cxx __attribute__ ((__visibility__ ("default"))) {
+ }
+  template<typename _CharT, typename _Traits = std::char_traits<_CharT>,          typename _Alloc = std::allocator<_CharT> > class basic_string     : public __gnu_cxx::__versa_string<_CharT, _Traits, _Alloc> {
+ };
+  namespace base {
+ template<typename T> struct remove_pointer { typedef T type; };
+ }
+  template<typename It> class fooPtrArrayIterator     : public std::iterator<           std::random_access_iterator_tag,           typename base::remove_pointer<               typename base::remove_pointer<It>::type>::type> {
+ };
+
+  class fooVoidPtrArray {
+  public:   fooVoidPtrArray() : space_(initial_space_),                         avail_(INIT_SIZE),                         alloc_(0),                         size_(0) {   }
+    inline int size() const {     return size_;   }
+   static const int INIT_SIZE = 4;
+   void* initial_space_[INIT_SIZE];
+   void** space_;
+   int avail_;
+   int alloc_;
+   int size_;
+ };
+  template <class T> class fooPtrArray : public fooVoidPtrArray {
+  public:   typedef fooPtrArrayIterator<T**> iterator;
+   inline const T& get(int i) const {     (static_cast<void> (0));     (static_cast<void> (0));     return *(space_as_T()[i]);   }
+  private:   inline T** space_as_T() const { return reinterpret_cast<T**>(space_); }
+ };
+  class blah_1  { 
+  public:   blah_1() {};
+   virtual const char* bar() const;
+ };
+ blah_1 th;
+
+  class blah_3 {
+  public:   friend class _blah_3_NameInfoInit;
+   virtual const char* bar() const;
+ };
+  class blah_2 { 
+  private:   unsigned char hasbits_[2];
+   fooPtrArray<blah_3> exp_info_;
+   static const int kHasBit_ar = 8;
+   const blah_1& ar() const;
+   const blah_1& au() const;
+  public:   bool has_ar() const { return ((hasbits_[kHasBit_ar/8] & (1 << (kHasBit_ar & 0x7)))!=0); }
+   virtual const char* bar() const __attribute__((noinline));
+ };
+  inline const blah_1& blah_2::ar() const {
+  return th;
+ }
+  inline const blah_1& blah_2::au ()const {
+  return th;
+ }
+  const char* blah_1::bar() const {
+   return 0;
+ }
+  const char* blah_3::bar() const {
+   const char* e = __null;
+   return e;
+ }
+  const char* blah_2::bar() const {
+   const char* e = __null;
+   if (has_ar() && ((e=ar().blah_1::bar()))) return e;
+   for (int i = 0, n = exp_info_.size(); i < n; i++) {     
+     if ((e=exp_info_.get(i).blah_3::bar())) return e;   
+    }
+   return e;
+ }
+
+
+ int main(void)
+ {
+  blah_2 b;
+
+  b.bar();
+  return 0;
+ } /* { dg-message  "note: file" ""  } */

Attachment: cl2
Description: Binary data

Attachment: cl3
Description: Binary data


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]