When the coverage and branch profiling preprocessing happens it can
choose to split certain nodes with labels to accuractely track coverage.
These shapes were recongized, but this causes some expressions to be
counted wrong as this graph may be isomorphic with graphs that have not
been split, for example for this snippet:

    if (b)
        return 0;

    if (a)
    {
        if (b)
        {
            b += 2;
            if (b & 0xFF)
                c++;
            else
                c--;

            return c;
        }
        else
        {
            a++;
        }
        c += 10;
    }
    else
    {
        b++;
    }

This is a nice approach since the need for detecting this pattern only
comes from the coverage instrumentation splitting the edge in the first
place.
---
 gcc/profile.cc                         |  8 ++++++++
 gcc/testsuite/gcc.misc-tests/gcov-19.c |  7 +++----
 gcc/tree-profile.cc                    | 24 ++++--------------------
 3 files changed, 15 insertions(+), 24 deletions(-)

diff --git a/gcc/profile.cc b/gcc/profile.cc
index 4407bb0683d..00975661b0d 100644
--- a/gcc/profile.cc
+++ b/gcc/profile.cc
@@ -1257,6 +1257,9 @@ branch_prob (bool thunk)
                  basic_block new_bb = split_edge (e);
                  edge ne = single_succ_edge (new_bb);
                  ne->goto_locus = e->goto_locus;
+                 /* Mark the edge with IGNORE so condition coverage knows that
+                    the edge split occurred and this should be contracted.  */
+                 ne->flags |= EDGE_IGNORE;
                }
              if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL))
                   && e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
@@ -1608,6 +1611,11 @@ branch_prob (bool thunk)
   /* Commit changes done by instrumentation.  */
   gsi_commit_edge_inserts ();
 
+  /* Unset all EDGE_IGNORE set in this pass.  */
+  FOR_EACH_BB_FN (bb, cfun)
+    for (edge e : bb->succs)
+      e->flags &= ~EDGE_IGNORE;
+
   coverage_end_function (lineno_checksum, cfg_checksum);
   if (flag_branch_probabilities
       && (profile_status_for_fn (cfun) == PROFILE_READ))
diff --git a/gcc/testsuite/gcc.misc-tests/gcov-19.c 
b/gcc/testsuite/gcc.misc-tests/gcov-19.c
index 8d6eb610af2..1c671f7e186 100644
--- a/gcc/testsuite/gcc.misc-tests/gcov-19.c
+++ b/gcc/testsuite/gcc.misc-tests/gcov-19.c
@@ -299,12 +299,11 @@ then:
     return;
 begin:
     /* Evaluates to if (a || b || c) x = 1 */
-    if (a)         /* conditions(5/6) true(2) */
-                   /* conditions(end) */
+    if (a)     /* conditions(2/2) */
        goto then;
-    else if (b)
+    else if (b)        /* conditions(2/2) */
        goto then;
-    else if (c)
+    else if (c) /* conditions(1/2) true(0) */
        goto then;
 }
 
diff --git a/gcc/tree-profile.cc b/gcc/tree-profile.cc
index ab96b872db3..3fc78ff8ace 100644
--- a/gcc/tree-profile.cc
+++ b/gcc/tree-profile.cc
@@ -344,32 +344,16 @@ contract_edge_up (edge e)
     |  \|
     T   F
 
-    This function recognizes this shape and returns the "merges" the split
-    outcome block by returning their common successor.  In all other cases it 
is
-    the identity function.  */
+    When this split happens it flags the edge with EDGE_IGNORE.  */
 basic_block
 merge_split_outcome (basic_block b)
 {
     if (!single (b->succs))
        return b;
-    if (!single (b->preds))
-       return b;
-
-    const unsigned flag = single_edge (b->preds)->flags & EDGE_CONDITION;
-    if (!flag)
-       return b;
-
     edge e = single_edge (b->succs);
-    for (edge pred : e->dest->preds)
-    {
-       if (!(pred->flags & EDGE_FALLTHRU))
-           return b;
-       if (!single (pred->src->preds))
-           return b;
-       if (!(single_edge (pred->src->preds)->flags & flag))
-           return b;
-    }
-    return e->dest;
+    if (e->flags & EDGE_IGNORE)
+       return e->dest;
+    return b;
 }
 
 
-- 
2.30.2

Reply via email to