On 01/06/2018 01:01 AM, Jeff Law wrote:
> On 01/05/2018 01:59 PM, Aldy Hernandez wrote:
>> This fixes the code that verifies that none of the uninitialized paths
>> reaching a PHI are actually taken (uninit_uses_cannot_happen).
>>
>> As discussed in the PR, this is done by fixing the predicate analysis in
>> tree-ssa-uninit.c so that the set of predicates reaching the use of a
>> PHI take into the entire path from the entry block.  Previously we were
>> starting at the immediate dominator of the PHI (for some obscure reason,
>> or brain fart on my part).
>>
>> Fixing the predicate analysis to go all the way back to ENTRY, uncovers
>> some latent limitations in compute_control_dep_chain().  I've fixed
>> those as well.
>>
>> Note, I don't know why we were excluding basic blocks with a small (< 2)
>> number of successors, but the exclusion was keeping us from looking at
>> the ENTRY block.  If this was by design, I can special case the ENTRY
>> block.  Similarly, convert_control_dep_chain_into_preds() was ignoring
>> basic blocks with no instructions.  This was making us skip the ENTRY
>> block, which is just an empty forwarder block.  Again, if this was by
>> design, I can just special case the ENTRY block.
> It's almost certainly the case they're ignored because a block with a
> single successor never controls whether or not we execute some other
> block in the CFG.  A block with a single successor always transfers
> control to that successor.
> 
> But the existence of a block with a single successor shouldn't stop
> building the control dependence chain.  We should just proceed into that
> single successor and continue to build the control dependency chain.
> 
> 
>>
>> Finally, I've split up the dumping routine into member functions so we
>> can get finer grained debugging help.
>>
>> Tested on x86-64 Linux.
>>
>> OK for trunk?
>>
>> curr.patch
>>
>>
>> gcc/
>>
>>      PR middle-end/81897
>>      * tree-ssa-uninit.c (compute_control_dep_chain): Do not bail on
>>      basic blocks with a small number of successors.
>>      (convert_control_dep_chain_into_preds): Special case the entry
>>      block.
>>      (dump_predicates): Split apart into...
>>      (dump_pred_chain): ...here...
>>      (dump_pred_info): ...and here.
>>      (can_one_predicate_be_invalidated_p): Add debugging printfs.
>>      (can_chain_union_be_invalidated_p): Return TRUE if any predicate
>>      was invalidated.
>>      (uninit_uses_cannot_happen): Avoid unnecessary if
>>      convert_control_dep_chain_into_preds yielded nothing.
> So given that we regressed last time we poked around in here, I'm
> looking this much deeper.  For reference 78566 and 61409 were the bugs
> we looked at last cycle.
> 
> 
>> diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c
>> index 6930a243deb..58590a7763b 100644
>> --- a/gcc/tree-ssa-uninit.c
>> +++ b/gcc/tree-ssa-uninit.c
>> @@ -543,9 +543,6 @@ compute_control_dep_chain (basic_block bb, basic_block 
>> dep_bb,
>>    bool found_cd_chain = false;
>>    size_t cur_chain_len = 0;
>>  
>> -  if (EDGE_COUNT (bb->succs) < 2)
>> -    return false;
> So the worry here would be a block with no successors, but I think the
> right thing will happen in this case.
> 
> 
>> @@ -671,11 +668,9 @@ convert_control_dep_chain_into_preds (vec<edge> 
>> *dep_chains,
>>        e = one_cd_chain[j];
>>        guard_bb = e->src;
>>        gsi = gsi_last_bb (guard_bb);
>> +      /* Ignore empty BBs as they're basically forwarder blocks.  */
>>        if (gsi_end_p (gsi))
>> -        {
>> -          has_valid_pred = false;
>> -          break;
>> -        }
>> +        continue;
>>        cond_stmt = gsi_stmt (gsi);
>>        if (is_gimple_call (cond_stmt) && EDGE_COUNT (e->src->succs) >= 2)
>>          /* Ignore EH edge.  Can add assertion on the other edge's flag.  */
> ISTM that you want to use empty_block_p (bb) && single_succ_p (bb) to
> detect the forwarder block.  Otherwise ISTM that labels and debug
> statements would affect the uninit analysis.
> 
> 
>> @@ -2287,14 +2308,17 @@ can_chain_union_be_invalidated_p (pred_chain_union 
>> uninit_pred,
>>  {
>>    if (uninit_pred.is_empty ())
>>      return false;
>> +  if (dump_file && dump_flags & TDF_DETAILS)
>> +    dump_predicates (NULL, uninit_pred,
>> +                 "Testing if anything here can be invalidated: ");
>>    for (size_t i = 0; i < uninit_pred.length (); ++i)
>>      {
>>        pred_chain c = uninit_pred[i];
>>        for (size_t j = 0; j < c.length (); ++j)
>> -    if (!can_one_predicate_be_invalidated_p (c[j], use_guard))
>> -      return false;
>> +    if (can_one_predicate_be_invalidated_p (c[j], use_guard))
>> +      return true;
>>      }
>> -  return true;
>> +  return false;
>>  }
> This seems close, but not quite right.
> 
> We want to prove (in the most general form) that for all paths from
> ENTRY to the PHI which result in the PHI taking on an uninitialized
> value that there is no viable path from the PHI to any use of the PHI
> result.
> 
> We prune that search space by only allowing a single path from the PHI
> to uses of the PHI.  So we just have to prove that for all paths from
> ENTRY to the PHI where the PHI takes on an uninitialized value the
> single path from the PHI to the use is not viable.
> 
> USE_GUARD is the predicate chain for that one and only one path from the
> PHI to the use of the PHI.
> 
> UNINIT_PRED is a vector of predicate chains for the uninitialized PHI
> arguments.  One predicate chain per uninitialized PHI argument (C in the
> loop).
> 
> Thus we have to iterate over each predicate chain in UNINIT_PRED and
> verify that it can be invalidated by USE_GUARD.  If any predicate chain
> in UNINIT_PRED can not be invalidated by USE_GUARD, then the chain union
> can not be invalidated.
> 
> So we walk a given chain from UNINIT_PRED, C.  If we are unable to
> invalidate any predicates in C we must return false.  If we are able to
> invalidate a predicate in C, then we go to the next chain within
> UNINIT_PRED and try to invalidate it.  If we are successful in
> invalidating all the chains in UNINIT_PRED, then and only then can we
> return true.
> 
> So I think this ought to look like:
> 
> 
>   for (size_t i = 0; i < uninit_pred.length (); ++i)
>     {
>       pred_chain c = uninit_pred[i];
>       size_t j;
>       for (j = 0; j < c.length (); ++j)
>         if (can_one_predicate_be_invalidated_p (c[j], use_guard))
>           break;
> 
>       /* If we were unable to invalidate any predicate in C, then there
>          is a viable path from entry to the PHI where the PHI takes
>          an uninitialized value and continues to a use of the PHI.  */
>       if (j == c.length ())
>         return false;
>     }
> 
>   /* We were able to invalidate each chain within UNINIT_PRED.  */
>   return true;
> 
> Thoughts?
I went ahead and fixed up the empty block test, and the invalidation
code above.  Bootstrapped and regression tested on x86_64.  Installing
on the trunk.

jeff
commit 13f02fc3e1c3abedeb1f551fc232b432f233988a
Author: Jeff Law <l...@torsion.usersys.redhat.com>
Date:   Sun Jan 7 00:24:07 2018 -0500

            PR middle-end/81897
            * tree-ssa-uninit.c (compute_control_dep_chain): Do not bail on
            basic blocks with a small number of successors.
            (convert_control_dep_chain_into_preds): Improve handling of
            forwarder blocks.
            (dump_predicates): Split apart into...
            (dump_pred_chain): ...here...
            (dump_pred_info): ...and here.
            (can_one_predicate_be_invalidated_p): Add debugging printfs.
            (can_chain_union_be_invalidated_p): Improve check for invalidation
            of paths.
            (uninit_uses_cannot_happen): Avoid unnecessary if
            convert_control_dep_chain_into_preds yielded nothing.
    
            PR middle-end/81897
            * gcc.dg/uninit-pr81897.c: New test.

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index cd9971d0d1a..f6fe407051a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,19 @@
+2018-01-06  Aldy Hernandez  <al...@redhat.com>
+
+       PR middle-end/81897
+       * tree-ssa-uninit.c (compute_control_dep_chain): Do not bail on
+       basic blocks with a small number of successors.
+       (convert_control_dep_chain_into_preds): Improve handling of
+       forwarder blocks.
+       (dump_predicates): Split apart into...
+       (dump_pred_chain): ...here...
+       (dump_pred_info): ...and here.
+       (can_one_predicate_be_invalidated_p): Add debugging printfs.
+       (can_chain_union_be_invalidated_p): Improve check for invalidation
+       of paths.
+       (uninit_uses_cannot_happen): Avoid unnecessary if
+       convert_control_dep_chain_into_preds yielded nothing.
+
 2018-01-06  Martin Sebor  <mse...@redhat.com>
 
        PR tree-optimization/83640
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 3b1548dde20..218e7821df7 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2018-01-06  Aldy Hernandez  <al...@redhat.com>
+
+       PR middle-end/81897
+       * gcc.dg/uninit-pr81897.c: New test.
+
 2018-01-06  Martin Sebor  <mse...@redhat.com>
 
        PR tree-optimization/83640
diff --git a/gcc/testsuite/gcc.dg/uninit-pr81897.c 
b/gcc/testsuite/gcc.dg/uninit-pr81897.c
new file mode 100644
index 00000000000..0323050839d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/uninit-pr81897.c
@@ -0,0 +1,24 @@
+/* { dg-do compile }  */
+/* { dg-options "-O2 -Wuninitialized" } */
+
+int f(void);
+static inline void rcu_read_unlock(void)
+{
+        static _Bool __warned;
+        if (f() && !__warned && !f()) {
+                __warned = 1;
+        }
+}
+int inet6_rtm_getroute(void)
+{
+        int dst;
+        int fibmatch = f();
+
+        if (!fibmatch)
+                dst = f();
+        rcu_read_unlock();
+        if (fibmatch)
+                dst = 0;
+
+        return dst;
+}
diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c
index 6930a243deb..38239476286 100644
--- a/gcc/tree-ssa-uninit.c
+++ b/gcc/tree-ssa-uninit.c
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa.h"
 #include "params.h"
 #include "tree-cfg.h"
+#include "cfghooks.h"
 
 /* This implements the pass that does predicate aware warning on uses of
    possibly uninitialized variables.  The pass first collects the set of
@@ -543,9 +544,6 @@ compute_control_dep_chain (basic_block bb, basic_block 
dep_bb,
   bool found_cd_chain = false;
   size_t cur_chain_len = 0;
 
-  if (EDGE_COUNT (bb->succs) < 2)
-    return false;
-
   if (*num_calls > PARAM_VALUE (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS))
     return false;
   ++*num_calls;
@@ -671,11 +669,9 @@ convert_control_dep_chain_into_preds (vec<edge> 
*dep_chains,
          e = one_cd_chain[j];
          guard_bb = e->src;
          gsi = gsi_last_bb (guard_bb);
-         if (gsi_end_p (gsi))
-           {
-             has_valid_pred = false;
-             break;
-           }
+         /* Ignore empty BBs as they're basically forwarder blocks.  */
+         if (empty_block_p (guard_bb) && single_succ_p (guard_bb))
+           continue;
          cond_stmt = gsi_stmt (gsi);
          if (is_gimple_call (cond_stmt) && EDGE_COUNT (e->src->succs) >= 2)
            /* Ignore EH edge.  Can add assertion on the other edge's flag.  */
@@ -916,38 +912,49 @@ find_def_preds (pred_chain_union *preds, gphi *phi)
   return has_valid_pred;
 }
 
+/* Dump a pred_info.  */
+
+static void
+dump_pred_info (pred_info one_pred)
+{
+  if (one_pred.invert)
+    fprintf (dump_file, " (.NOT.) ");
+  print_generic_expr (dump_file, one_pred.pred_lhs);
+  fprintf (dump_file, " %s ", op_symbol_code (one_pred.cond_code));
+  print_generic_expr (dump_file, one_pred.pred_rhs);
+}
+
+/* Dump a pred_chain.  */
+
+static void
+dump_pred_chain (pred_chain one_pred_chain)
+{
+  size_t np = one_pred_chain.length ();
+  for (size_t j = 0; j < np; j++)
+    {
+      dump_pred_info (one_pred_chain[j]);
+      if (j < np - 1)
+       fprintf (dump_file, " (.AND.) ");
+      else
+       fprintf (dump_file, "\n");
+    }
+}
+
 /* Dumps the predicates (PREDS) for USESTMT.  */
 
 static void
 dump_predicates (gimple *usestmt, pred_chain_union preds, const char *msg)
 {
-  size_t i, j;
-  pred_chain one_pred_chain = vNULL;
   fprintf (dump_file, "%s", msg);
-  print_gimple_stmt (dump_file, usestmt, 0);
-  fprintf (dump_file, "is guarded by :\n\n");
+  if (usestmt)
+    {
+      print_gimple_stmt (dump_file, usestmt, 0);
+      fprintf (dump_file, "is guarded by :\n\n");
+    }
   size_t num_preds = preds.length ();
-  /* Do some dumping here:  */
-  for (i = 0; i < num_preds; i++)
+  for (size_t i = 0; i < num_preds; i++)
     {
-      size_t np;
-
-      one_pred_chain = preds[i];
-      np = one_pred_chain.length ();
-
-      for (j = 0; j < np; j++)
-       {
-         pred_info one_pred = one_pred_chain[j];
-         if (one_pred.invert)
-           fprintf (dump_file, " (.NOT.) ");
-         print_generic_expr (dump_file, one_pred.pred_lhs);
-         fprintf (dump_file, " %s ", op_symbol_code (one_pred.cond_code));
-         print_generic_expr (dump_file, one_pred.pred_rhs);
-         if (j < np - 1)
-           fprintf (dump_file, " (.AND.) ");
-         else
-           fprintf (dump_file, "\n");
-       }
+      dump_pred_chain (preds[i]);
       if (i < num_preds - 1)
        fprintf (dump_file, "(.OR.)\n");
       else
@@ -2259,12 +2266,19 @@ normalize_preds (pred_chain_union preds, gimple 
*use_or_def, bool is_use)
 }
 
 /* Return TRUE if PREDICATE can be invalidated by any individual
-   predicate in WORKLIST.  */
+   predicate in USE_GUARD.  */
 
 static bool
 can_one_predicate_be_invalidated_p (pred_info predicate,
                                    pred_chain use_guard)
 {
+  if (dump_file && dump_flags & TDF_DETAILS)
+    {
+      fprintf (dump_file, "Testing if this predicate: ");
+      dump_pred_info (predicate);
+      fprintf (dump_file, "\n...can be invalidated by a USE guard of: ");
+      dump_pred_chain (use_guard);
+    }
   for (size_t i = 0; i < use_guard.length (); ++i)
     {
       /* NOTE: This is a very simple check, and only understands an
@@ -2273,7 +2287,15 @@ can_one_predicate_be_invalidated_p (pred_info predicate,
         invalidate with say [i > 5] or [i == 8].  There is certainly
         room for improvement here.  */
       if (pred_neg_p (predicate, use_guard[i]))
-       return true;
+       {
+         if (dump_file && dump_flags & TDF_DETAILS)
+           {
+             fprintf (dump_file, "  Predicate was invalidated by: ");
+             dump_pred_info (use_guard[i]);
+             fputc ('\n', dump_file);
+           }
+         return true;
+       }
     }
   return false;
 }
@@ -2287,12 +2309,22 @@ can_chain_union_be_invalidated_p (pred_chain_union 
uninit_pred,
 {
   if (uninit_pred.is_empty ())
     return false;
+  if (dump_file && dump_flags & TDF_DETAILS)
+    dump_predicates (NULL, uninit_pred,
+                    "Testing if anything here can be invalidated: ");
   for (size_t i = 0; i < uninit_pred.length (); ++i)
     {
       pred_chain c = uninit_pred[i];
-      for (size_t j = 0; j < c.length (); ++j)
-       if (!can_one_predicate_be_invalidated_p (c[j], use_guard))
-         return false;
+      size_t j;
+      for (j = 0; j < c.length (); ++j)
+       if (can_one_predicate_be_invalidated_p (c[j], use_guard))
+         break;
+
+      /* If we were unable to invalidate any predicate in C, then there
+        is a viable path from entry to the PHI where the PHI takes
+        an uninitialized value and continues to a use of the PHI.  */
+      if (j == c.length ())
+       return false;
     }
   return true;
 }
@@ -2334,7 +2366,7 @@ uninit_uses_cannot_happen (gphi *phi, unsigned 
uninit_opnds,
 
       /* Build the control dependency chain for uninit operand `i'...  */
       uninit_preds = vNULL;
-      if (!compute_control_dep_chain (find_dom (e->src),
+      if (!compute_control_dep_chain (ENTRY_BLOCK_PTR_FOR_FN (cfun),
                                      e->src, dep_chains, &num_chains,
                                      &cur_chain, &num_calls))
        {
@@ -2342,10 +2374,16 @@ uninit_uses_cannot_happen (gphi *phi, unsigned 
uninit_opnds,
          break;
        }
       /* ...and convert it into a set of predicates.  */
-      convert_control_dep_chain_into_preds (dep_chains, num_chains,
-                                           &uninit_preds);
+      bool has_valid_preds
+       = convert_control_dep_chain_into_preds (dep_chains, num_chains,
+                                               &uninit_preds);
       for (size_t j = 0; j < num_chains; ++j)
        dep_chains[j].release ();
+      if (!has_valid_preds)
+       {
+         ret = false;
+         break;
+       }
       simplify_preds (&uninit_preds, NULL, false);
       uninit_preds = normalize_preds (uninit_preds, NULL, false);
 

Reply via email to