new file mode 100644
@@ -0,0 +1,75 @@
+/* { dg-options "-fprofile-conditions -ftest-coverage -O2 -c" } */
+
+int id (int);
+int idp (int *);
+int err;
+
+/* This becomes problematic only under optimization for the case when the
+ compiler cannot inline the function but have to generate a call. It is not
+ really interesting to run, only see if it builds. Notably, both the
+ function calls and the return values are important to construct a
+ problematic graph.
+
+ This test is also a good example of where optimization makes condition
+ coverage unpredictable, but not unusable. If this is built without
+ optimization the conditions work as you would expect from reading the
+ source. */
+/* Adapted from cpio-2.14 gnu/utmens.c lutimens (). */
+int
+mcdc001 (int *v)
+{
+ int adjusted;
+ int adjustment_needed = 0;
+
+ int *ts = v ? &adjusted : 0; /* conditions(0/4) true(0 1) false(0 1) */
+ /* conditions(end) */
+ if (ts)
+ adjustment_needed = idp (ts);
+ if (adjustment_needed < 0)
+ return -1;
+
+ if (adjustment_needed) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ if (adjustment_needed != 3) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return -1;
+ if (ts) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return 0;
+ }
+
+ if (adjustment_needed && idp (&adjusted)) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return -1;
+ if (adjusted) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return idp (ts);
+
+ return -1;
+}
+
+/* This failed when the candidate set internal/contracted-past nodes were not
+ properly marked as reachable in the candidate reduction phase. */
+/* Adapted from cpio-2.14 gnu/mktime.c mktime_internal (). */
+int
+mcdc002 ()
+{
+ int a;
+ if (idp (&a)) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ if (id (a)) /* conditions(0/2) true(0/2) true(0) false(0) */
+ /* conditions(end) */
+ goto exit;
+
+ if (err) /* conditions(0/2) true(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return -1;
+ }
+
+exit:
+ return a;
+}
+
+/* { dg-final { run-gcov conditions { --conditions gcov-23.c } } } */
@@ -279,23 +279,27 @@ single_edge (const vec<edge, va_gc> *edges)
merged.
Only chains of single-exit single-entry nodes that end with a condition
- should be contracted. */
+ should be contracted. If the optional bitset G is passed, the intermediate
+ "contracted-past" nodes will be recorded, which is only meaningful if the
+ non-source edge is returned. */
edge
-contract_edge (edge e)
+contract_edge (edge e, sbitmap G = nullptr)
{
edge source = e;
while (true)
{
basic_block dest = e->dest;
- if (!single (dest->preds))
- return source;
if (e->flags & EDGE_DFS_BACK)
return source;
- if (block_conditional_p (dest))
- return e;
if (dest->index == EXIT_BLOCK)
return source;
+ if (!single (dest->preds))
+ return source;
+ if (block_conditional_p (dest))
+ return e;
+ if (G)
+ bitmap_set_bit (G, dest->index);
e = single_edge (dest->succs);
if (!e)
return source;
@@ -673,6 +677,8 @@ cond_reachable_from (basic_block p, basic_block q, sbitmap expr,
if (!all_preds_conditional_p (dest, expr))
continue;
+ if (dest != e->dest)
+ contract_edge (e, expr);
bitmap_set_bit (expr, dest->index);
out.safe_push (dest);
}
@@ -692,8 +698,14 @@ neighborhood (const vec<basic_block>& blocks, sbitmap G, vec<basic_block>& out)
basic_block dest = contract_edge (e)->dest;
if (bitmap_bit_p (G, dest->index))
continue;
- if (!out.contains (dest))
- out.safe_push (dest);
+ if (out.contains (dest))
+ continue;
+ /* There was a contraction, so replay it but this time also record
+ the intermediate nodes, so that the reachable set becomes
+ complete. This is necessary when reducing the candidate set. */
+ if (dest != e->dest)
+ contract_edge (e, G);
+ out.safe_push (dest);
}
}
@@ -784,12 +796,6 @@ isolate_expression (conds_ctx &ctx, basic_block p, vec<basic_block>& out)
bitmap_clear_bit (expr, NG[j]->index);
bitmap_set_bit (expr, b->index);
NG.safe_push (b);
- /* It could be that the predecessor edge from the ancestor
- was contracted past, in which case we need to mark it to
- make sure its ancestors_of do not immediately terminate. */
- for (edge e : b->preds)
- if (ctx.index_map[e->src->index] > ctx.index_map[p->index])
- bitmap_set_bit (reachable, e->src->index);
}
}
}
@@ -813,6 +819,13 @@ isolate_expression (conds_ctx &ctx, basic_block p, vec<basic_block>& out)
bitmap_clear (reachable);
for (basic_block b : G)
bitmap_set_bit (reachable, b->index);
+
+ /* Contracted-past nodes in the subgraph must be re-marked, otherwise
+ the next neighborhood may be computed wrong. */
+ for (basic_block b : G)
+ for (edge e : b->succs)
+ if (bitmap_bit_p (reachable, contract_edge (e)->dest->index))
+ contract_edge (e, reachable);
}
out.safe_splice (G);