aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite
diff options
context:
space:
mode:
authorJørgen Kvalsvik <j@lambda.is>2023-12-05 12:59:40 +0100
committerJørgen Kvalsvik <j@lambda.is>2024-04-04 20:28:44 +0200
commit08a52331803f66a4aaeaedd278436ca8eac57b50 (patch)
tree0827170eb48377be2b7888ab2bb8e9012b89b55c /gcc/testsuite
parentb7bd2ec73d66f7487bc8842b24daecaa802a72e6 (diff)
downloadgcc-08a52331803f66a4aaeaedd278436ca8eac57b50.zip
gcc-08a52331803f66a4aaeaedd278436ca8eac57b50.tar.gz
gcc-08a52331803f66a4aaeaedd278436ca8eac57b50.tar.bz2
Add condition coverage (MC/DC)
This patch adds support in gcc+gcov for modified condition/decision coverage (MC/DC) with the -fcondition-coverage flag. MC/DC is a type of test/code coverage and it is particularly important for safety-critical applicaitons in industries like aviation and automotive. Notably, MC/DC is required or recommended by: * DO-178C for the most critical software (Level A) in avionics. * IEC 61508 for SIL 4. * ISO 26262-6 for ASIL D. From the SQLite webpage: Two methods of measuring test coverage were described above: "statement" and "branch" coverage. There are many other test coverage metrics besides these two. Another popular metric is "Modified Condition/Decision Coverage" or MC/DC. Wikipedia defines MC/DC as follows: * Each decision tries every possible outcome. * Each condition in a decision takes on every possible outcome. * Each entry and exit point is invoked. * Each condition in a decision is shown to independently affect the outcome of the decision. In the C programming language where && and || are "short-circuit" operators, MC/DC and branch coverage are very nearly the same thing. The primary difference is in boolean vector tests. One can test for any of several bits in bit-vector and still obtain 100% branch test coverage even though the second element of MC/DC - the requirement that each condition in a decision take on every possible outcome - might not be satisfied. https://sqlite.org/testing.html#mcdc MC/DC comes in different flavors, the most important being unique cause MC/DC and masking MC/DC. This patch implements masking MC/DC, which is works well with short circuiting semantics, and according to John Chilenski's "An Investigation of Three Forms of the Modified Condition Decision Coverage (MCDC) Criterion" (2001) is as good as unique cause at catching bugs. Whalen, Heimdahl, and De Silva "Efficient Test Coverage Measurement for MC/DC" describes an algorithm for finding the masking table from an AST walk, but my algorithm figures this out by analyzing the control flow graph. The CFG is considered a reduced ordered binary decision diagram and an input vector a path through the BDD, which is recorded. Specific edges will mask ("null out") the contribution from earlier path segments, which can be determined by finding short circuit endpoints. Masking is most easily understood as circuiting of terms in the reverse-ordered Boolean function, and the masked conditions do not affect the decision like short-circuited conditions do not affect the decision. A tag/discriminator mapping from gcond->uid is created during gimplification and made available through the function struct. The values are unimportant as long as basic conditions constructed from a single Boolean expression are given the same identifier. This happens in the breaking down of ANDIF/ORIF trees, so the coverage generally works well for frontends that create such trees. Like Whalen et al this implementation records coverage in fixed-size bitsets which gcov knows how to interpret. Recording conditions only requires a few bitwise operations per condition and is very fast, but comes with a limit on the number of terms in a single boolean expression; the number of bits in a gcov_unsigned_type (which is usually typedef'd to uint64_t). For most practical purposes this is acceptable, and by default a warning will be issued if gcc cannot instrument the expression. This is a practical limitation in the implementation, and not a limitation of the algorithm, so support for more conditions can be supported by introducing arbitrary-sized bitsets. In action it looks pretty similar to the branch coverage. The -g short opt carries no significance, but was chosen because it was an available option with the upper-case free too. gcov --conditions: 3: 17:void fn (int a, int b, int c, int d) { 3: 18: if ((a && (b || c)) && d) conditions covered 3/8 condition 0 not covered (true false) condition 1 not covered (true) condition 2 not covered (true) condition 3 not covered (true) 1: 19: x = 1; -: 20: else 2: 21: x = 2; 3: 22:} gcov --conditions --json-format: "conditions": [ { "not_covered_false": [ 0 ], "count": 8, "covered": 3, "not_covered_true": [ 0, 1, 2, 3 ] } ], Expressions with constants may be heavily rewritten before it reaches the gimplification, so constructs like int x = a ? 0 : 1 becomes _x = (_a == 0). From source you would expect coverage, but it gets neither branch nor condition coverage. The same applies to expressions like int x = 1 || a which are simply replaced by a constant. The test suite contains a lot of small programs and functions. Some of these were designed by hand to test for specific behaviours and graph shapes, and some are previously-failed test cases in other programs adapted into the test suite. gcc/ChangeLog: * builtins.cc (expand_builtin_fork_or_exec): Check condition_coverage_flag. * collect2.cc (main): Add -fno-condition-coverage to OBSTACK. * common.opt: Add new options -fcondition-coverage and -Wcoverage-too-many-conditions. * doc/gcov.texi: Add --conditions documentation. * doc/invoke.texi: Add -fcondition-coverage documentation. * function.cc (free_after_compilation): Free cond_uids. * function.h (struct function): Add cond_uids. * gcc.cc: Link gcov on -fcondition-coverage. * gcov-counter.def (GCOV_COUNTER_CONDS): New. * gcov-dump.cc (tag_conditions): New. * gcov-io.h (GCOV_TAG_CONDS): New. (GCOV_TAG_CONDS_LENGTH): New. (GCOV_TAG_CONDS_NUM): New. * gcov.cc (class condition_info): New. (condition_info::condition_info): New. (condition_info::popcount): New. (struct coverage_info): New. (add_condition_counts): New. (output_conditions): New. (print_usage): Add -g, --conditions. (process_args): Likewise. (output_intermediate_json_line): Output conditions. (read_graph_file): Read condition counters. (read_count_file): Likewise. (file_summary): Print conditions. (accumulate_line_info): Accumulate conditions. (output_line_details): Print conditions. * gimplify.cc (next_cond_uid): New. (reset_cond_uid): New. (shortcut_cond_r): Set condition discriminator. (tag_shortcut_cond): New. (gimple_associate_condition_with_expr): New. (shortcut_cond_expr): Set condition discriminator. (gimplify_cond_expr): Likewise. (gimplify_function_tree): Call reset_cond_uid. * ipa-inline.cc (can_early_inline_edge_p): Check condition_coverage_flag. * ipa-split.cc (pass_split_functions::gate): Likewise. * passes.cc (finish_optimization_passes): Likewise. * profile.cc (struct condcov): New declaration. (cov_length): Likewise. (cov_blocks): Likewise. (cov_masks): Likewise. (cov_maps): Likewise. (cov_free): Likewise. (instrument_decisions): New. (read_thunk_profile): Control output to file. (branch_prob): Call find_conditions, instrument_decisions. (init_branch_prob): Add total_num_conds. (end_branch_prob): Likewise. * tree-core.h (struct tree_exp): Add condition_uid. * tree-profile.cc (struct conds_ctx): New. (CONDITIONS_MAX_TERMS): New. (EDGE_CONDITION): New. (topological_cmp): New. (index_of): New. (single_p): New. (single_edge): New. (contract_edge_up): New. (struct outcomes): New. (conditional_succs): New. (condition_index): New. (condition_uid): New. (masking_vectors): New. (emit_assign): New. (emit_bitwise_op): New. (make_top_index_visit): New. (make_top_index): New. (paths_between): New. (struct condcov): New. (cov_length): New. (cov_blocks): New. (cov_masks): New. (cov_maps): New. (cov_free): New. (find_conditions): New. (struct counters): New. (find_counters): New. (resolve_counter): New. (resolve_counters): New. (instrument_decisions): New. (tree_profiling): Check condition_coverage_flag. (pass_ipa_tree_profile::gate): Likewise. * tree.h (SET_EXPR_UID): New. (EXPR_COND_UID): New. libgcc/ChangeLog: * libgcov-merge.c (__gcov_merge_ior): New. gcc/testsuite/ChangeLog: * lib/gcov.exp: Add condition coverage test function. * g++.dg/gcov/gcov-18.C: New test. * gcc.misc-tests/gcov-19.c: New test. * gcc.misc-tests/gcov-20.c: New test. * gcc.misc-tests/gcov-21.c: New test. * gcc.misc-tests/gcov-22.c: New test. * gcc.misc-tests/gcov-23.c: New test.
Diffstat (limited to 'gcc/testsuite')
-rw-r--r--gcc/testsuite/g++.dg/gcov/gcov-18.C282
-rw-r--r--gcc/testsuite/gcc.misc-tests/gcov-19.c1737
-rw-r--r--gcc/testsuite/gcc.misc-tests/gcov-20.c22
-rw-r--r--gcc/testsuite/gcc.misc-tests/gcov-21.c16
-rw-r--r--gcc/testsuite/gcc.misc-tests/gcov-22.c103
-rw-r--r--gcc/testsuite/gcc.misc-tests/gcov-23.c361
-rw-r--r--gcc/testsuite/lib/gcov.exp259
7 files changed, 2778 insertions, 2 deletions
diff --git a/gcc/testsuite/g++.dg/gcov/gcov-18.C b/gcc/testsuite/g++.dg/gcov/gcov-18.C
new file mode 100644
index 0000000..620aef1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gcov/gcov-18.C
@@ -0,0 +1,282 @@
+/* { dg-options "--coverage -fcondition-coverage -std=c++11" } */
+/* { dg-do run { target native } } */
+
+#include <vector>
+#include <stdexcept>
+
+class nontrivial_destructor
+{
+public:
+ explicit nontrivial_destructor (int v) : val (v) {}
+ ~nontrivial_destructor () {}
+
+ explicit operator bool() const { return bool(val); }
+
+ int val;
+};
+
+int identity (int x) { return x; }
+int throws (int) { throw std::runtime_error("exception"); }
+
+int
+throw_if (int x)
+{
+ if (x) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ throw std::runtime_error("exception");
+ return x;
+}
+
+/* Used for side effects to insert nodes in conditional bodies etc. */
+int x = 0;
+
+/* Conditionals work in the presence of non-trivial destructors. */
+void
+mcdc001a (int a)
+{
+ nontrivial_destructor v (a);
+
+ if (v.val > 0) /* conditions(2/2) */
+ x = v.val;
+ else
+ x = -v.val;
+}
+
+/* Non-trivial destructor in-loop temporary. */
+nontrivial_destructor
+mcdc002a (int a, int b)
+{
+ for (int i = 0; i < a; i++) /* conditions(2/2) */
+ {
+ nontrivial_destructor tmp (a);
+ if (tmp.val % b) /* conditions(2/2) */
+ return nontrivial_destructor (0);
+ x += i;
+ } /* conditions(suppress) */
+ /* conditions(end) */
+
+ return nontrivial_destructor (a * b);
+}
+
+/* Conditional in constructor. */
+void
+mcdc003a (int a)
+{
+ class C
+ {
+ public:
+ explicit C (int e) : v (e)
+ {
+ if (e) /* conditions(1/2) false(0) */
+ v = x - e;
+ }
+ int v;
+ };
+
+ C c (a);
+ if (c.v > 2) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ x = c.v + a;
+}
+
+/* Conditional in destructor. */
+void
+mcdc004a (int a)
+{
+ class C
+ {
+ public:
+ explicit C (int e) : v (e) {}
+ ~C ()
+ {
+ if (v) /* conditions(2/2) */
+ x = 2 * v;
+ }
+ int v;
+ };
+
+ C c (a);
+ x = 1; // arbitrary action between ctor+dtor
+}
+
+/* Conditional in try. */
+void
+mcdc005a (int a)
+{
+ try
+ {
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ x = 2 * identity (a);
+ else
+ x = 1;
+ }
+ catch (...)
+ {
+ x = 0;
+ }
+}
+
+/* Conditional in catch. */
+void
+mcdc006a (int a) {
+ try
+ {
+ throws (a);
+ }
+ catch (std::exception&)
+ {
+ if (a) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ x = identity (a);
+ else
+ x = 0;
+ }
+}
+
+void
+mcdc006b (int a)
+{
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ throws (a);
+ else
+ x = 1;
+}
+
+void
+mcdc006c (int a) try
+{
+ throws (a);
+}
+catch (...) {
+ if (a) /* conditions(2/2) */
+ x = 5;
+}
+
+/* Temporary with destructor as term. */
+void
+mcdc007a (int a, int b)
+{
+ x = a && nontrivial_destructor (b); /* conditions(3/4) false(1) destructor() */
+}
+
+void
+mcdc007b (int a, int b)
+{
+ if (a || throw_if (b)) /* conditions(3/4) true(1) destructor() */
+ x = -1;
+ else
+ x = 1;
+}
+
+void
+mcdc007c (int a, int b)
+{
+ if (throw_if (a) || throw_if (b)) /* conditions(2/4) true(0 1) destructor() */
+ x = -1;
+ else
+ x = 1;
+}
+
+/* Destructor with delete. */
+void
+mcdc008a (int a)
+{
+ class C
+ {
+ public:
+ int size = 5;
+ int* ptr = nullptr;
+
+ explicit C (int v) : size (v + 5), ptr (new int[size]) /* conditions(suppress) */
+ /* conditions(end) */
+ {
+ for (int i = 0; i < size; i++) /* conditions(2/2) */
+ ptr[i] = i + 1;
+ }
+ ~C()
+ {
+ // delete with implicit nullptr check
+ delete ptr; /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ }
+ };
+
+ C c (a);
+ if (c.ptr[a + 1]) /* conditions(1/2) false(0) */
+ x = a;
+}
+
+/* Templates. */
+template <typename T>
+void
+mcdc009a (T a)
+{
+ if (a > 0) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ x += 2;
+}
+
+/* constexpr. */
+
+/* Compile-time evaluated branches do not contribute to coverage. */
+constexpr int
+mcdc010a (int a, int b)
+{
+ return a > b ? 1 : 2; /* conditions(1/2) true(0) */
+ /* conditions(end) */
+}
+
+/* Compile-time only evaluated functions do not show up in the compiled program
+ and gets no coverage at all. If this would generate output unexpectedly it
+ would trigger a test failure ("unexpected output"). */
+constexpr int
+mcdc010b (int a, int b)
+{
+ return a > b ? 1 : 2;
+}
+
+int
+main (void)
+{
+ mcdc001a (0);
+ mcdc001a (1);
+
+ mcdc002a (1, 1);
+ mcdc002a (1, 2);
+
+ mcdc003a (1);
+
+ mcdc004a (0);
+ mcdc004a (1);
+
+ mcdc005a (0);
+
+ mcdc006a (1);
+
+ mcdc006b (0);
+
+ mcdc006c (0);
+ mcdc006c (1);
+
+ mcdc007a (0, 0);
+ mcdc007a (1, 1);
+
+ mcdc007b (0, 0);
+ mcdc007b (1, 0);
+
+ mcdc007c (0, 0);
+
+ mcdc008a (1);
+
+ mcdc009a (1);
+
+ /* Use identity () so that this is not constexpr eval'd. */
+ int v1 = mcdc010a (identity (2), 4);
+ constexpr int v2 = mcdc010a (4, 2);
+
+ constexpr int v3 = mcdc010b (2, 4);
+}
+
+/* { dg-final { run-gcov conditions { --conditions gcov-18.C } } } */
diff --git a/gcc/testsuite/gcc.misc-tests/gcov-19.c b/gcc/testsuite/gcc.misc-tests/gcov-19.c
new file mode 100644
index 0000000..17f1fb4
--- /dev/null
+++ b/gcc/testsuite/gcc.misc-tests/gcov-19.c
@@ -0,0 +1,1737 @@
+/* { dg-options "-fcondition-coverage -ftest-coverage" } */
+/* { dg-do run { target native } } */
+
+/* Some side effect to stop branches from being pruned. */
+int x = 0;
+
+int id (int x) { return x; }
+int inv (int x) { return !x; }
+
+/* || works. */
+void
+mcdc001a (int a, int b)
+{
+ if (a || b) /* conditions(1/4) true(0) false(0 1) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc001b (int a, int b)
+{
+ if (a || b) /* conditions(3/4) true(0) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc001c (int a, int b)
+{
+ if (a || b) /* conditions(4/4) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc001d (int a, int b, int c)
+{
+ if (a || b || c) /* conditions(2/6) false(0 1 2) true(2) */
+ /* conditions(end) */
+ x = 1;
+}
+
+/* && works */
+void
+mcdc002a (int a, int b)
+{
+ if (a && b) /* conditions(1/4) true(0 1) false(0) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc002b (int a, int b)
+{
+ if (a && b) /* conditions(3/4) false(0) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc002c (int a, int b)
+{
+ if (a && b) /* conditions(4/4) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc002d (int a, int b, int c)
+{
+ if (a && b && c) /* conditions(4/6) false(0 2) */
+ /* conditions(end) */
+ x = 1;
+}
+
+/* Negation works. */
+void
+mcdc003a (int a, int b)
+{
+ if (!a || !b) /* conditions(2/4) false(0 1) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+/* Single conditionals with and without else. */
+void
+mcdc004a (int a)
+{
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc004b (int a)
+{
+ if (a) /* conditions(2/2) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc004c (int a)
+{
+ if (a) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ x = 1;
+}
+
+void
+mcdc004d (int a, int b, int c)
+{
+ if (a) /* conditions(2/2) */
+ {
+ if (b || c) /* conditions(1/4) true(1) false(0 1) */
+ x = a + b + c;
+ }
+}
+
+void
+mcdc004e (int a, int b, int c)
+{
+ if (a) /* conditions(2/2) */
+ {
+ if (b || c) /* conditions(1/4) true(1) false(0 1) */
+ /* conditions(end) */
+ x = a + b + c;
+ }
+ else
+ {
+ x = c;
+ }
+}
+
+void
+mcdc004f (int a, int b, int c)
+{
+ if (a) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ {
+ x = 1;
+ }
+ else if (b) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ x = 2;
+ if (c) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ x = 3;
+ }
+}
+
+/* mixing && and || works */
+void
+mcdc005a (int a, int b, int c)
+{
+ if ((a && b) || c) /* conditions(1/6) true(0 1) false(0 1 2) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc005b (int a, int b, int c, int d)
+{
+ /* This is where masking MC/DC gets unintuitive:
+
+ 1 1 0 0 => covers 1 (d = 0) as && 0 masks everything to the left
+ 1 0 0 0 => covers 2 (b = 0, c = 0) as (a && 0) masks a and d is never
+ evaluated. */
+ if ((a && (b || c)) && d) /* conditions(3/8) true(0 1 2 3) false(0) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc005c (int a, int b, int c, int d)
+{
+ if (a || (b && c) || d) /* conditions(2/8) true(0 3) false(0 1 2 3) */
+ /* conditions(end) */
+ x = a + b + c + d;
+}
+
+void
+mcdc005d (int a, int b, int c, int d)
+{
+ /* This test is quite significant - it has a single input
+ (1, 0, 0, 0) and tests specifically for when a multi-term left operand
+ is masked. d = 0 should mask a || b and for the input there are no other
+ sources for masking a (since b = 0). */
+ if ((a || b) && (c || d)) /* conditions(2/8) true(0 1 2 3) false(0 1) */
+ /* conditions(end) */
+ x = a + b;
+ else
+ x = c + d;
+}
+
+/* Mixing in constants kills the decision removes the term outright. */
+void
+mcdc005e (int a, int b)
+{
+ x += 0 && a;
+ x += a && 1;
+ x += 0 && a && b;
+ x += a && 1 && b; /* conditions(4/4) */
+ x += a && b && 0;
+ x += 0 && 1;
+ x += 1 && a;
+ x += a && 0;
+ x += 1 || a;
+ x += a || 0;
+ x += 1 || a || b;
+ x += a || 0 || b; /* conditions(4/4) */
+ x += a || b || 1;
+ x += 1 || 0;
+ x += 0 || a;
+ x += a || 1;
+}
+
+/* Nested conditionals. */
+void
+mcdc006a (int a, int b, int c, int d, int e)
+{
+ if (a) /* conditions(2/2) */
+ {
+ if (b && c) /* conditions(3/4) false(1) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+ }
+ else
+ {
+ if (c || d) /* conditions(2/4) true(0 1) */
+ /* conditions(end) */
+ x = 3;
+ else
+ x = 4;
+ }
+}
+
+void
+mcdc006b (int a, int b, int c)
+{
+ if (a) /* conditions(2/2) */
+ if (b) /* conditions(2/2) */
+ if (c) /* conditions(2/2) */
+ x = a + b + c;
+}
+
+void
+mcdc006c (int a, int b, int c)
+{
+ if (a) /* conditions(2/2) */
+ {
+ if (b) /*conditions(2/2) */
+ {
+ if (c) /* conditions(2/2) */
+ {
+ x = a + b + c;
+ }
+ }
+ else
+ {
+ x = b;
+ }
+ }
+ else
+ {
+ x = a;
+ }
+}
+
+void
+mcdc006d (int a, int b, int c)
+{
+ if (a) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ {
+ if (b) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ x = a + b;
+ if (c) /* conditions(2/2) */
+ /* conditions(end) */
+ x = a + b;
+ }
+}
+
+void
+mcdc006e (int a, int b, int c, int d)
+{
+ if ((a || b || c) && id (d)) /* conditions(4/8) true(0 1 2 3) false() */
+ /* conditions(end) */
+ x = 1;
+}
+
+/* else/if. */
+void
+mcdc007a (int a, int b, int c, int d)
+{
+ if (a) /* conditions(2/2) */
+ {
+ if (b) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+ }
+ else if (c) /* conditions(2/2) */
+ {
+ if (d) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ x = 3;
+ else
+ x = 4;
+ }
+}
+
+void
+mcdc007b (int a, int b, int c)
+{
+ goto begin;
+then:
+ x = 1;
+ return;
+begin:
+ if (a) /* conditions(2/2) */
+ goto then;
+ else if (b) /* conditions(2/2) */
+ goto then;
+ else if (c) /* conditions(1/2) true(0) */
+ goto then;
+}
+
+void
+mcdc007c (int a, int b, int c)
+{
+ goto begin;
+then1:
+ x = 1;
+ return;
+then2:
+ x = 1;
+ return;
+then3:
+ x = 1;
+ return;
+begin:
+ if (a) /* conditions(2/2) */
+ goto then1;
+ else if (b) /* conditions(2/2) */
+ goto then2;
+ else if (c) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ goto then3;
+}
+
+void
+noop () {}
+
+int
+mcdc007d (int a, int b, int c, int d, int e)
+{
+ noop ();
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ {
+ if (b || c) /* conditions(0/4) true(0 1) false(0 1) */
+ /* conditions(end) */
+ x = 2;
+ if (d) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return 1;
+ }
+ if (e) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ return 0;
+
+ return 2;
+}
+
+/* while loop. */
+void
+mcdc008a (int a)
+{
+ while (a < 10) /* conditions(2/2) */
+ x = a++;
+}
+
+void
+mcdc008b (int a)
+{
+ while (a > 10) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ x = a--;
+}
+
+void
+mcdc008c (int a)
+{
+ // should work, even with no body
+ while (a) /* conditions(2/2) */
+ break;
+}
+
+/* Multi-term loop conditional. */
+void
+mcdc008d (int a, int b, int c, int d)
+{
+ while ((a && (b || c)) && d) /* conditions(8/8) */
+ a = b = c = d = 0;
+}
+
+void
+mcdc009a (int a, int b)
+{
+ while (a > 0 && b > 0) /* conditions(3/4) false(1) */
+ /* conditions(end) */
+ x = a--;
+}
+
+void
+mcdc009b (int a, int b)
+{
+ while (a-- > 0 && b) {} /* conditions(2/4) true(0 1) */
+ /* conditions(end) */
+}
+
+/* for loop. */
+void
+mcdc010a (int a, int b)
+{
+ for (int i = 0; i < b; i++) /* conditions(2/2) */
+ {
+ if (a < b) /* conditions(2/2) */
+ x = 1;
+ else
+ x = a += 2;
+ }
+}
+
+void
+mcdc010b ()
+{
+ for (int a = 0; a <= 1; ++a) /* conditions(2/2) */
+ {
+ x = a;
+ }
+}
+
+int
+mcdc010c (int a, int b, int c)
+{
+ for (;a || b || c;) /* conditions(4/6) true(0 2) */
+ /* conditions(end) */
+ return 1;
+ return 0;
+}
+
+
+int always (int x) { (void) x; return 1; }
+
+/* No-condition infinite loops. */
+void
+mcdc010d (int a)
+{
+ for (;;)
+ {
+ if (always(a)) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ {
+ x = a;
+ break;
+ }
+ x += a + 1;
+ }
+}
+
+/* Conditionals without control flow constructs work. */
+void
+mcdc011a (int a, int b, int c)
+{
+ x = (a && b) || c; /* conditions(5/6) false(1) */
+ /* conditions(end) */
+}
+
+/* Sequential expressions are handled independently. */
+void
+mcdc012a (int a, int b, int c)
+{
+ if (a || b) /* conditions(3/4) true(0) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+
+ if (c) /* conditions(2/2) */
+ x = 1;
+}
+
+/* Cannot ever satisfy (masking) MC/DC, even with all input combinations,
+ because not all variables independently affect the decision. */
+void
+mcdc013a (int a, int b, int c)
+{
+ (void)b;
+ /* Specification: (a && b) || c
+ The implementation does not match the specification. This has branch
+ coverage, but not MC/DC. */
+ if ((a && !c) || c) /* conditions(5/6) false(1) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+void
+mcdc014a ()
+{
+ int conds[64] = { 0 };
+ /* conditions(64/128) true(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63) */
+ x = conds[ 0] || conds[ 1] || conds[ 2] || conds[ 3] || conds[ 4] ||
+ conds[ 5] || conds[ 6] || conds[ 7] || conds[ 8] || conds[ 9] ||
+ conds[10] || conds[11] || conds[12] || conds[13] || conds[14] ||
+ conds[15] || conds[16] || conds[17] || conds[18] || conds[19] ||
+ conds[20] || conds[21] || conds[22] || conds[23] || conds[24] ||
+ conds[25] || conds[26] || conds[27] || conds[28] || conds[29] ||
+ conds[30] || conds[31] || conds[32] || conds[33] || conds[34] ||
+ conds[35] || conds[36] || conds[37] || conds[38] || conds[39] ||
+ conds[40] || conds[41] || conds[42] || conds[43] || conds[44] ||
+ conds[45] || conds[46] || conds[47] || conds[48] || conds[49] ||
+ conds[50] || conds[51] || conds[52] || conds[53] || conds[54] ||
+ conds[55] || conds[56] || conds[57] || conds[58] || conds[59] ||
+ conds[60] || conds[61] || conds[62] || conds[63]
+ ; /* conditions(end) */
+}
+
+/* Early returns. */
+void
+mcdc015a (int a, int b)
+{
+ if (a) /* conditions(2/2) */
+ return;
+
+ if (b) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ x = 1;
+}
+
+void
+mcdc015b (int a, int b)
+{
+ for (int i = 5; i > a; i--) /* conditions(2/2) */
+ {
+ if (i == b) /* conditions(2/2) */
+ return;
+ x = i;
+ }
+}
+
+void
+mcdc015c (int a, int b)
+{
+ for (int i = 5; i > a; i--) /* conditions(2/2) */
+ {
+ if (i == b) /* conditions(2/2) */
+ {
+ x = 0;
+ return;
+ }
+ else
+ {
+ x = 1;
+ return;
+ }
+
+ x = i;
+ }
+}
+
+/* Early returns, gotos. */
+void
+mcdc015d (int a, int b, int c)
+{
+ if (a) return; /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ if (id (b)) return; /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ if (id (c)) return; /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+}
+
+
+/* Nested loops. */
+void
+mcdc016a (int a, int b)
+{
+ for (int i = 0; i < a; i++) /* conditions(2/2) */
+ for (int k = 0; k < b; k++) /* conditions(2/2) */
+ x = i + k;
+}
+
+void
+mcdc016b (int a, int b)
+{
+ for (int i = 0; i < a; i++) /* conditions(2/2) */
+ {
+ if (a > 5) /* conditions(2/2) */
+ break;
+
+ for (int k = 0; k < b; k++) /* conditions(2/2) */
+ x = i + k;
+ }
+}
+
+void
+mcdc016c (int a, int b)
+{
+ for (int i = 0; i < a; i++) /* conditions(2/2) */
+ {
+ if (a > 5) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ return;
+
+ for (int k = 0; k < b; k++) /* conditions(2/2) */
+ x = i + k;
+ }
+}
+
+void
+mcdc016d (int a, int b)
+{
+ for (int i = 0; i < a; i++) /* conditions(2/2) */
+ {
+ for (int k = 0; k < 5; k++) /* conditions(2/2) */
+ {
+ if (b > 5) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ return;
+ x = i + k;
+ }
+
+ }
+}
+
+/* do-while loops */
+void
+mcdc017a (int a)
+{
+ do
+ {
+ a--;
+ } while (a > 0); /* conditions(2/2) */
+}
+
+void
+mcdc017b (int a, int b)
+{
+ do
+ {
+ /* This call is important; it can add more nodes to the body in the
+ CFG, which changes how close exits and breaks are to the loop
+ conditional. */
+ noop ();
+ a--;
+ if (b) /* conditions(2/2) */
+ break;
+
+ } while (a > 0); /* conditions(2/2) */
+}
+
+void
+mcdc017c (int a, int b)
+{
+ int left = 0;
+ int right = 0;
+ int n = a + b;
+ do
+ {
+ if (a) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ {
+ left = a > left ? b : left; /* conditions(2/2) */
+ }
+ if (b) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ {
+ right = b > right ? a : right; /* conditions(2/2) */
+ }
+ } while (n-- > 0); /* conditions(2/2) */
+}
+
+void
+mcdc017d (int a, int b, int c)
+{
+ do
+ {
+ a--;
+ } while (a > 0 && b && c); /* conditions(0/6) true(0 1 2) false(0 1 2) */
+ /* conditions(end) */
+}
+
+/* Collection of odd cases lifted-and-adapted from real-world code. */
+int
+mcdc018a (int a, int b, int c, int d, int e, int f, int g, int len)
+{
+ int n;
+ /* adapted from zlib/gz_read */
+ do
+ {
+ n = -1;
+ if (n > len) /* conditions(2/2) */
+ n = len;
+
+ if (b) /* conditions(2/2) */
+ {
+ if (b < 5) /* conditions(2/2) */
+ x = 1;
+ noop();
+ }
+ else if (c && d) /* conditions(3/4) false(1) */
+ /* conditions(end) */
+ {
+ x = 2;
+ break;
+ }
+ else if (e || f) /* conditions(2/4) false(0 1) */
+ /* conditions(end) */
+ {
+ if (id(g)) /* conditions(2/2) */
+ return 0;
+ continue;
+ }
+ } while (a-- > 0); /* conditions(2/2) */
+
+ return 1;
+}
+
+void
+mcdc018b (int a, int b, int c)
+{
+ int n;
+ while (a) /* conditions(2/2) */
+ {
+ /* else block does not make a difference for the problem, but ensures
+ loop termination. */
+ if (b) /* conditions(2/2) */
+ n = c ? 0 : 0; // does not show up in CFG (embedded in the block)
+ else
+ n = 0;
+ a = n;
+ }
+}
+
+/* Adapted from zlib/compress2. */
+void
+mcdc018c (int a, int b)
+{
+ int err;
+ do
+ {
+ a = inv (a);
+ err = a;
+ } while (err); /* conditions(1/2) true(0) */
+ /* conditions(end) */
+
+ a = id (a);
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ x *= a + 1;
+}
+
+/* Too many conditions, coverage gives up. */
+void
+mcdc019a ()
+{
+ int conds[65] = { 0 };
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wcoverage-too-many-conditions"
+ x = conds[ 0] || conds[ 1] || conds[ 2] || conds[ 3] || conds[ 4] ||
+ conds[ 5] || conds[ 6] || conds[ 7] || conds[ 8] || conds[ 9] ||
+ conds[10] || conds[11] || conds[12] || conds[13] || conds[14] ||
+ conds[15] || conds[16] || conds[17] || conds[18] || conds[19] ||
+ conds[20] || conds[21] || conds[22] || conds[23] || conds[24] ||
+ conds[25] || conds[26] || conds[27] || conds[28] || conds[29] ||
+ conds[30] || conds[31] || conds[32] || conds[33] || conds[34] ||
+ conds[35] || conds[36] || conds[37] || conds[38] || conds[39] ||
+ conds[40] || conds[41] || conds[42] || conds[43] || conds[44] ||
+ conds[45] || conds[46] || conds[47] || conds[48] || conds[49] ||
+ conds[50] || conds[51] || conds[52] || conds[53] || conds[54] ||
+ conds[55] || conds[56] || conds[57] || conds[58] || conds[59] ||
+ conds[60] || conds[61] || conds[62] || conds[63] || conds[64]
+ ;
+ #pragma GCC diagnostic pop
+}
+
+/* Ternary. */
+void
+mcdc020a (int a)
+{
+ // special case, this can be reduced to:
+ // _1 = argc != 0;
+ // e = (int) _1;
+ x = a ? 1 : 0;
+
+ // changing to different int makes branch
+ x = a ? 2 : 1; /* conditions(2/2) */
+}
+
+void
+mcdc020b (int a, int b)
+{
+ x = (a || b) ? 1 : 0; /* conditions(3/4) true(1) */
+ /* conditions(end) */
+}
+
+void
+mcdc020c (int a, int b)
+{
+ x = a ? 0
+ : b ? 1 /* conditions(2/2) */
+ : 2; /* conditions(1/2) false(0) */
+ /* conditions(end) */
+}
+
+int
+mcdc020d (int b, int c, int d, int e, int f)
+{
+ return ((b ? c : d) && e && f); /* conditions(7/10) true(2) false(3 4) */
+ /* conditions(end) */
+}
+
+/* Infinite loop (no exit-edge), this should not be called, but it should
+ compile fine. */
+void
+mcdc021a ()
+{
+ while (1)
+ ;
+}
+
+/* Computed goto can give all sorts of problems, including difficult path
+ contractions. */
+void
+mcdc021b ()
+{
+ void *op = &&dest;
+dest:
+ if (op) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ goto * 0;
+}
+
+int __sigsetjmp ();
+
+/* This should compile, but not called. */
+void
+mcdc021c ()
+{
+ while (x) /* conditions(0/2) true(0) false(0)*/
+ /* conditions(end) */
+ __sigsetjmp ();
+}
+
+/* If edges are not properly contracted the a && id (b) will be interpreted as
+ two independent expressions. */
+void
+mcdc021d (int a, int b, int c, int d)
+{
+ if (a && id (b)) /* conditions(1/4) true(0 1) false(0) */
+ /* conditions(end) */
+ x = 1;
+ else if (c && id (d)) /* conditions(1/4) true(0 1) false(0) */
+ /* conditions(end) */
+ x = 2;
+ else
+ x = 3;
+}
+
+/* Adapted from linux arch/x86/tools/relocs.c
+ With poor edge contracting this became an infinite loop. */
+void
+mcdc022a (int a, int b)
+{
+ for (int i = 0; i < 5; i++) /* conditions(2/2) */
+ {
+ x = i;
+ for (int j = i; j < 5; j++) /* conditions(2/2) */
+ {
+ if (id (id (a)) || id (b)) /* conditions(3/4) true(0) */
+ /* conditions(end) */
+ continue;
+ b = inv(b);
+ }
+ }
+}
+
+int
+mcdc022b (int a)
+{
+ int devt;
+ if (a) /* conditions(2/2) */
+ {
+ x = a * 2;
+ if (x != a / 10 || x != a % 10) /* conditions(1/4) true(1) false(0 1) */
+ /* conditions(end) */
+ return 0;
+ } else {
+ devt = id (a);
+ if (devt) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ return 0;
+ }
+
+ return devt;
+}
+
+/* Adapted from linux arch/x86/events/intel/ds.c
+
+ It broken sorting so that the entry block was not the first node after
+ sorting. */
+void
+mcdc022c (int a)
+{
+ if (!a) /* conditions(2/2) */
+ return;
+
+ for (int i = 0; i < 5; i++) /* conditions(2/2) */
+ {
+ if (id (a + i) || inv (a - 1)) /* conditions(1/4) false(0 1) true(1) */
+ /* conditions(end) */
+ x = a + i;
+ if (inv (a)) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ break;
+ }
+}
+
+void
+mcdc022d (int a)
+{
+ int i;
+ for (i = 0; i < id (a); i++) /* conditions(1/2) false(0) */
+ {
+ if (!inv (a)) /* conditions(1/2) false(0)*/
+ /* conditions(end) */
+ break;
+ }
+
+ if (i < a) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ x = a + 1;
+}
+
+/* Adapted from openssl-3.0.1/crypto/cmp/cmp_msg.c ossl_cmp_error_new (). */
+void
+mcdc022e (int a, int b, int c, int d)
+{
+ if (a || b) /* conditions(1/4) true(0) false(0 1) */
+ /* conditions(end) */
+ {
+ if (always (c)) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ goto err;
+ d++;
+ }
+
+ if (d) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ goto err;
+ return;
+
+err:
+ noop ();
+}
+
+/* 023 specifically tests that masking works correctly, which gets complicated
+ fast with a mix of operators and deep subexpressions. These tests violates
+ the style guide slightly to emphasize the nesting. They all share the same
+ implementation and only one input is given to each function to obtain clean
+ coverage results. */
+void
+mcdc023a (int a, int b, int c, int d, int e, int f, int g, int h, int i, int k,
+ int l, int m, int n)
+{
+ // [a m n] = 0, [b, ...] = 1
+ // a is masked by b and the remaining terms should be short circuited
+ if (/* conditions(1/24) true(0 2 3 4 5 6 7 8 9 10 11) false(0 1 2 3 4 5 6 7 8 9 10 11) */
+ /* conditions(end) */
+ (a || b)
+ || ( ((c && d) || (e && (f || g) && h))
+ && (k || l)
+ && (m || n)))
+ x = a + b;
+ else
+ x = b + c;
+}
+
+void
+mcdc023b (int a, int b, int c, int d, int e, int f, int g, int h, int i, int k,
+ int l, int m, int n)
+{
+ // [a b d h] = 0, [c, ...] = 1
+ // h = 0 => false but does not mask (a || b) or (c && d). d = 0 masks c.
+ if (/* conditions(4/24) true(0 1 2 3 4 5 6 7 8 9 10 11) false(2 4 5 6 8 9 10 11) */
+ /* conditions(end) */
+ (a || b)
+ || ( ((c && d) || (e && (f || g) && h))
+ && (k || l)
+ && (m || n)))
+ x = a + b;
+ else
+ x = b + c;
+}
+
+void
+mcdc023c (int a, int b, int c, int d, int e, int f, int g, int h, int i, int k,
+ int l, int m, int n)
+{
+ /* [m n a b] = 0, [...] = 1
+ n,m = 0 should mask all other terms than a, b */
+ if (/* conditions(4/24) true(0 1 2 3 4 5 6 7 8 9 10 11) false(2 3 4 5 6 7 8 9) */
+ /* conditions(end) */
+ (a || b)
+ || ( ((c && d) || (e && (f || g) && h))
+ && (k || l)
+ && (m || n)))
+ x = a + b;
+ else
+ x = b + c;
+}
+
+void
+mcdc023d (int a, int b, int c, int d, int e, int f, int g, int h, int i, int k,
+ int l, int m, int n)
+{
+ /* [a b] = 0, [h, ...] = 1
+ n,m = 0 should mask all other terms than a, b */
+ if (/* conditions(4/24) true(0 1 2 3 4 5 6 7 8 9 10 11) false(2 3 4 5 6 7 10 11) */
+ /* conditions(end) */
+ (a || b)
+ || ( ((c && d) || (e && (f || g) && h))
+ && (k || l)
+ && (m || n)))
+ x = a + b;
+ else
+ x = b + c;
+}
+
+void
+mcdc023e (int a, int b, int c, int d, int e, int f, int g, int h, int i, int k,
+ int l, int m, int n)
+{
+ /* [a b d] = 0, [c h, ...] = 1
+ h = 1 should mask c, d, leave other terms intact.
+ If [k l m n] were false then h itself would be masked.
+ [a b] are masked as collateral by [m n]. */
+ if (/* conditions(5/24) true(0 1 2 3 6 9 11) false(0 1 2 3 4 5 6 7 8 9 10 11) */
+ /* conditions(end) */
+ (a || b)
+ || ( ((c && d) || (e && (f || g) && h))
+ && (k || l)
+ && (m || n)))
+ x = a + b;
+ else
+ x = b + c;
+}
+
+void
+mcdc023f (int a, int b, int c, int d, int e, int f, int g, int h, int i, int k,
+ int l, int m, int n)
+{
+ /* [a b c f g] = 0, [e, ...] = 1
+ [f g] = 0 should mask e, leave [c d] intact. */
+ if (/* conditions(5/24) true(0 1 2 3 4 5 6 7 8 9 10 11) false(3 4 7 8 9 10 11) */
+ /* conditions(end) */
+ (a || b)
+ || ( ((c && d) || (e && (f || g) && h))
+ && (k || l)
+ && (m || n)))
+ x = a + b;
+ else
+ x = b + c;
+}
+
+void
+mcdc023g (int a, int b, int c, int d, int e, int f, int g, int h, int i, int k,
+ int l, int m, int n)
+{
+ /* [a b d f g] = 0, [e c, ...] = 1
+ Same as 023f but with [c d] flipped so d masks c rather than c
+ short-circuits. This should not be lost. */
+ if (/* conditions(5/24) true(0 1 2 3 4 5 6 7 8 9 10 11) false(2 4 7 8 9 10 11) */
+ /* conditions(end) */
+ (a || b)
+ || ( ((c && d) || (e && (f || g) && h))
+ && (k || l)
+ && (m || n)))
+ x = a + b;
+ else
+ x = b + c;
+}
+
+/* Gotos, return, labels can make odd graphs. It is important that conditions
+ are assigned to the right expression, and that there are no miscounts. In
+ these tests values may be re-used, as checking things like masking an
+ independence is done in other test cases and not so useful here. */
+void
+mcdc024a (int a, int b)
+{
+ /* This is a reference implementation without the labels, which should not
+ alter behavior. */
+ if (a && b) /* conditions(2/4) true(0 1) */
+ /* conditions(end) */
+ {
+ x = 1;
+ }
+ else
+ {
+ x = 2;
+ }
+
+ if (a || b) /* conditions(2/4) false(0 1) */
+ /* conditions(end) */
+ {
+ x = 1;
+ }
+ else
+ {
+ x = 2;
+ }
+}
+
+void
+mcdc024b (int a, int b)
+{
+ if (a && b) /* conditions(2/4) true(0 1) */
+ /* conditions(end) */
+ {
+label1:
+ x = 1;
+ }
+ else
+ {
+ x = 2;
+ }
+
+ if (a || b) /* conditions(2/4) false(0 1) */
+ /* conditions(end) */
+ {
+label2:
+ x = 1;
+ }
+ else
+ {
+ x = 2;
+ }
+}
+
+void
+mcdc024c (int a, int b)
+{
+
+ if (a && b) /* conditions(2/4) true(0 1) */
+ /* conditions(end) */
+ {
+ x = 1;
+ }
+ else
+ {
+label1:
+ x = 2;
+ }
+
+ if (a || b) /* conditions(2/4) false(0 1) */
+ /* conditions(end) */
+ {
+ x = 1;
+ }
+ else
+ {
+label2:
+ x = 2;
+ }
+}
+
+void
+mcdc024d (int a, int b)
+{
+ if (a && b) /* conditions(2/4) true(0 1) */
+ /* conditions(end) */
+ {
+label1:
+ x = 1;
+ }
+ else
+ {
+label2:
+ x = 2;
+ }
+
+ if (a || b) /* conditions(2/4) false(0 1) */
+ /* conditions(end) */
+ {
+label3:
+ x = 1;
+ }
+ else
+ {
+label4:
+ x = 2;
+ }
+}
+
+int
+mcdc024e (int a, int b, int c)
+{
+ /* Graphs can get complicated with the innermost returns and else-less if,
+ so we must make sure these conditions are counted correctly. */
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ {
+ if (b) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ if (c) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return 1;
+ else
+ return 2;
+ }
+
+ if (a) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return 3;
+ }
+
+ return 5;
+}
+
+/* Nested else-less ifs with inner returns needs to be counted right, which
+ puts some pressure on the expression isolation. */
+int
+mcdc024f (int a, int b, int c)
+{
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ {
+ if (b) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ if (c) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ if (a) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return 1;
+ else
+ return 2;
+ }
+
+ if (a) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return 3;
+ }
+
+ if (b) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return 4;
+ }
+ return 5;
+}
+
+int
+mcdc024g (int a, int b, int c)
+{
+ if (b) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ return 0;
+
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ {
+ if (b) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ b += 2;
+ if (b & 0xFF) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ c++;
+
+ return c;
+ }
+ c += 10;
+ }
+ return 1;
+}
+
+
+int
+mcdc024h (int a, int b, int c)
+{
+ if (b) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ goto inner;
+
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ ++a;
+
+
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ {
+ if (b) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+inner:
+ b += 2;
+ if (b & 0xFF) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ c++;
+
+ return c;
+ }
+ c += 10;
+ }
+ return 1;
+}
+
+int
+mcdc024i (int a, int b, int c)
+{
+fst:
+ b++;
+snd:
+ b++;
+
+ if (b > 10) /* conditions(2/2) */
+ /* conditions(end) */
+ goto end;
+
+ if (b < 5) /* conditions(2/2) */
+ /* conditions(end) */
+ goto fst;
+ else
+ goto snd;
+
+end:
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ ++a;
+
+
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ {
+ if (b) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ b += 2;
+ if (b & 0xFF) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ c++;
+
+ return c;
+ }
+ c += 10;
+ }
+ return 1;
+}
+
+/* Adapted from alsa-lib 1.2.8 src/control/control.c. If two expressions share
+ an outcome with bypass nodes they would be handled twice. */
+int
+mcdc025a (int a, int b, int c)
+{
+ int err;
+ if (id (a)) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ {
+ if (b) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return -1;
+ }
+ else
+ {
+ err = id (c);
+ if (err > 0) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ return err;
+ }
+ err = id (a);
+ return err;
+}
+
+/* Boolean expressions in function call parameters. These tests are all built
+ with a reference expression which should behave the same as the function
+ call versions. */
+int
+mcdc026a (int a, int b, int c, int d, int e)
+{
+ int cad = c && d; /* conditions(4/4) */
+ /* conditions(end) */
+ int x = a && b && cad && e; /* conditions(5/8) false(0 1 3) */
+ /* conditions(end) */
+ int y = a && b && id (c && d) && e; /* conditions(5/8; 4/4) false(0 1 3;;) */
+ /* conditions(end) */
+ return x + y;
+}
+
+int
+mcdc026b (int a, int b, int c, int d, int e)
+{
+ int dae = d && e; /* conditions(3/4) false(1) */
+ /* conditions(end) */
+ int x = a && b && c && dae; /* conditions(6/8) false(0 1) */
+ int y = a && b && c && id (d && e); /* conditions(6/8; 3/4) false(0 1; 1) */
+ /* conditions(end) */
+ return x + y;
+}
+
+int
+mcdc026c (int a, int b, int c, int d, int e)
+{
+ int cod = c || d; /* conditions(3/4) true(1) */
+ /* conditions(end) */
+ int x = a && b && cod && e; /* conditions(5/8) false(0 1 3) */
+ int y = a && b && id (c || d) && e; /* conditions(5/8; 3/4) true(;1) false(0 1 3;) */
+ /* conditions(end) */
+ return x+y;
+}
+
+int
+mcdc026d (int a, int b, int c, int d, int e)
+{
+ int aab = a && b; /* conditions(2/4) false(0 1) */
+ /* conditions(end) */
+ int cod = c || d; /* conditions(3/4) true(1) */
+ /* conditions(end) */
+ int x = aab && cod && e; /* conditions(4/6) false(0 2) */
+ /* conditions(end) */
+ int y = id (a && b) && id (c || d) && e; /* conditions(2/4;4/6;3/4) true(;;1) false(0 1;0 2;;) */
+ /* conditions(end) */
+ return x + y;
+}
+
+int
+mcdc026e (int a, int b, int c, int d, int e)
+{
+ int cod = c || d; /* conditions(3/4) true(1) */
+ /* conditions(end) */
+ int dae = d && e; /* conditions(3/4) false(1) */
+ /* conditions(end) */
+ int aacod = a && cod; /* conditions(3/4) false(0)*/
+ /* conditions(end) */
+ int x = aacod && dae; /* conditions(4/4) */
+ /* conditions(end) */
+ int y = id (a && id (c || d)) && id (d && e); /* conditions(3/4; 3/4; 4/4; 3/4) true(;1;;) false(0;;;1) */
+ /* conditions(end) */
+ return x + y;
+}
+
+int main ()
+{
+ mcdc001a (0, 1);
+
+ mcdc001b (0, 1);
+ mcdc001b (0, 0);
+
+ mcdc001c (0, 1);
+ mcdc001c (0, 0);
+ mcdc001c (1, 1);
+
+ mcdc001d (1, 1, 1);
+ mcdc001d (0, 1, 0);
+
+ mcdc002a (1, 0);
+
+ mcdc002b (1, 0);
+ mcdc002b (1, 1);
+
+ mcdc002c (0, 0);
+ mcdc002c (1, 1);
+ mcdc002c (1, 0);
+
+ mcdc002d (1, 1, 1);
+ mcdc002d (1, 0, 0);
+
+ mcdc003a (0, 0);
+ mcdc003a (1, 0);
+
+ mcdc004a (0);
+ mcdc004b (0);
+ mcdc004b (1);
+ mcdc004c (1);
+
+ mcdc004d (0, 0, 0);
+ mcdc004d (1, 1, 1);
+
+ mcdc004e (0, 0, 0);
+ mcdc004e (1, 1, 1);
+
+ mcdc004f (1, 1, 1);
+
+ mcdc005a (1, 0, 1);
+
+ mcdc005b (1, 1, 0, 0);
+ mcdc005b (1, 0, 0, 0);
+
+ mcdc005c (0, 1, 1, 0);
+
+ mcdc005d (1, 0, 0, 0);
+
+ mcdc005e (0, 0);
+ mcdc005e (0, 1);
+ mcdc005e (1, 0);
+ mcdc005e (1, 1);
+
+ mcdc006a (0, 0, 0, 0, 0);
+ mcdc006a (1, 0, 0, 0, 0);
+ mcdc006a (1, 1, 1, 0, 0);
+
+ mcdc006b (0, 0, 0);
+ mcdc006b (1, 0, 0);
+ mcdc006b (1, 1, 0);
+ mcdc006b (1, 1, 1);
+
+ mcdc006c (0, 0, 0);
+ mcdc006c (1, 0, 0);
+ mcdc006c (1, 1, 0);
+ mcdc006c (1, 1, 1);
+
+ mcdc006d (1, 0, 0);
+ mcdc006d (1, 0, 1);
+
+ mcdc006e (0, 0, 0, 0);
+ mcdc006e (0, 0, 1, 0);
+ mcdc006e (0, 1, 0, 0);
+
+ mcdc007a (0, 0, 0, 0);
+ mcdc007a (1, 0, 0, 0);
+ mcdc007a (0, 0, 1, 0);
+
+ mcdc007b (0, 0, 0);
+ mcdc007b (0, 1, 1);
+ mcdc007b (1, 0, 1);
+
+ mcdc007c (0, 0, 0);
+ mcdc007c (0, 1, 1);
+ mcdc007c (1, 0, 1);
+
+ mcdc007d (0, 1, 0, 1, 1);
+
+ mcdc008a (0);
+
+ mcdc008b (0);
+
+ mcdc008c (0);
+ mcdc008c (1);
+
+ mcdc008d (0, 0, 0, 0);
+ mcdc008d (1, 0, 0, 0);
+ mcdc008d (1, 0, 1, 0);
+ mcdc008d (1, 0, 1, 1);
+ mcdc008d (1, 1, 1, 1);
+
+ mcdc009a (0, 0);
+ mcdc009a (1, 1);
+
+ mcdc009b (0, 0);
+ mcdc009b (1, 0);
+
+ mcdc010a (0, 0);
+ mcdc010a (0, 9);
+ mcdc010a (2, 1);
+
+ mcdc010b ();
+
+ mcdc010c (0, 0, 0);
+ mcdc010c (0, 1, 0);
+
+ mcdc010d (1);
+
+ mcdc011a (0, 0, 0);
+ mcdc011a (1, 1, 0);
+ mcdc011a (1, 0, 1);
+
+ mcdc012a (0, 0, 0);
+ mcdc012a (0, 1, 1);
+
+ mcdc013a (0, 0, 0);
+ mcdc013a (0, 0, 1);
+ mcdc013a (0, 1, 0);
+ mcdc013a (0, 1, 1);
+ mcdc013a (1, 0, 0);
+ mcdc013a (1, 0, 1);
+ mcdc013a (1, 1, 0);
+ mcdc013a (1, 1, 1);
+
+ mcdc014a ();
+
+ mcdc015a (0, 0);
+ mcdc015a (1, 0);
+
+ mcdc015b (0, 0);
+ mcdc015b (0, 1);
+ mcdc015b (6, 1);
+
+ mcdc015c (0, 0);
+ mcdc015c (0, 5);
+ mcdc015c (6, 1);
+
+ mcdc015d (1, 0, 0);
+
+ mcdc016a (5, 5);
+
+ mcdc016b (5, 5);
+ mcdc016b (6, 5);
+
+ mcdc016c (5, 5);
+
+ mcdc016d (1, 0);
+
+ mcdc017a (0);
+ mcdc017a (2);
+
+ mcdc017b (2, 0);
+ mcdc017b (0, 1);
+
+ mcdc017c (1, 1);
+
+ mcdc018a (0, 0, 1, 1, 0, 0, 0, 0);
+ mcdc018a (0, 1, 0, 0, 0, 0, 1, -2);
+ mcdc018a (0, 6, 0, 0, 0, 0, 1, -2);
+ mcdc018a (0, 6, 0, 0, 0, 0, 1, -2);
+ mcdc018a (0, 0, 0, 1, 0, 1, 1, 0);
+ mcdc018a (1, 0, 0, 0, 1, 1, 0, 0);
+
+ mcdc018b (1, 0, 0);
+ mcdc018b (1, 1, 0);
+
+ mcdc018c (1, 1);
+
+ mcdc019a ();
+
+ mcdc020a (0);
+ mcdc020a (1);
+
+ mcdc020b (0, 0);
+ mcdc020b (1, 0);
+
+ mcdc020c (0, 1);
+ mcdc020c (1, 1);
+
+ mcdc020d (0, 0, 0, 0, 0);
+ mcdc020d (1, 0, 0, 1, 1);
+ mcdc020d (1, 1, 0, 1, 1);
+
+ mcdc021d (1, 0, 1, 0);
+
+ mcdc022a (0, 0);
+
+ mcdc022b (0);
+ mcdc022b (1);
+
+ mcdc022c (0);
+ mcdc022c (1);
+
+ mcdc022d (1);
+ mcdc022e (0, 1, 1, 0);
+
+ mcdc023a (0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+ mcdc023b (0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1);
+ mcdc023c (0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0);
+ mcdc023d (0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1);
+ mcdc023e (0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1);
+ mcdc023f (0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1);
+ mcdc023g (0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1);
+
+ mcdc024a (0, 1);
+ mcdc024b (0, 1);
+ mcdc024c (0, 1);
+ mcdc024d (0, 1);
+ mcdc024a (1, 0);
+ mcdc024b (1, 0);
+ mcdc024c (1, 0);
+ mcdc024d (1, 0);
+
+ mcdc024e (0, 0, 0);
+ mcdc024f (0, 0, 0);
+ mcdc024g (0, 0, 0);
+ mcdc024h (0, 0, 0);
+ mcdc024i (0, 0, 0);
+
+ mcdc025a (0, 0, 1);
+
+ mcdc026a (1, 1, 1, 0, 1);
+ mcdc026a (1, 1, 0, 0, 1);
+ mcdc026a (1, 1, 1, 1, 1);
+
+ mcdc026b (1, 1, 1, 0, 1);
+ mcdc026b (1, 1, 0, 0, 1);
+ mcdc026b (1, 1, 1, 1, 1);
+
+ mcdc026c (1, 1, 1, 0, 1);
+ mcdc026c (1, 1, 0, 0, 1);
+ mcdc026c (1, 1, 1, 1, 1);
+
+ mcdc026d (1, 1, 1, 0, 1);
+ mcdc026d (1, 1, 0, 0, 1);
+ mcdc026d (1, 1, 1, 1, 1);
+
+ mcdc026e (1, 1, 1, 0, 1);
+ mcdc026e (1, 1, 0, 0, 1);
+ mcdc026e (1, 1, 1, 1, 1);
+}
+
+/* { dg-final { run-gcov conditions { --conditions gcov-19.c } } } */
diff --git a/gcc/testsuite/gcc.misc-tests/gcov-20.c b/gcc/testsuite/gcc.misc-tests/gcov-20.c
new file mode 100644
index 0000000..215faff
--- /dev/null
+++ b/gcc/testsuite/gcc.misc-tests/gcov-20.c
@@ -0,0 +1,22 @@
+/* { dg-options "-fcondition-coverage -ftest-coverage -fprofile-update=atomic" } */
+/* { dg-do run { target native } } */
+
+/* Some side effect to stop branches from being pruned */
+int x = 0;
+
+void
+conditions_atomic001 (int a, int b)
+{
+ if (a || b) /* conditions(1/4) true(0) false(0 1) */
+ /* conditions(end) */
+ x = 1;
+ else
+ x = 2;
+}
+
+int main ()
+{
+ conditions_atomic001 (0, 1);
+}
+
+/* { dg-final { run-gcov conditions { --conditions gcov-20.c } } } */
diff --git a/gcc/testsuite/gcc.misc-tests/gcov-21.c b/gcc/testsuite/gcc.misc-tests/gcov-21.c
new file mode 100644
index 0000000..3395422
--- /dev/null
+++ b/gcc/testsuite/gcc.misc-tests/gcov-21.c
@@ -0,0 +1,16 @@
+/* { dg-options "-fcondition-coverage" } */
+
+/* https://gcc.gnu.org/pipermail/gcc-patches/2022-April/592927.html */
+char trim_filename_name;
+int r;
+
+void trim_filename() {
+ if (trim_filename_name)
+ r = 123;
+ while (trim_filename_name)
+ ;
+}
+
+int main ()
+{
+}
diff --git a/gcc/testsuite/gcc.misc-tests/gcov-22.c b/gcc/testsuite/gcc.misc-tests/gcov-22.c
new file mode 100644
index 0000000..641791a
--- /dev/null
+++ b/gcc/testsuite/gcc.misc-tests/gcov-22.c
@@ -0,0 +1,103 @@
+/* { dg-options "-fcondition-coverage -ftest-coverage" } */
+/* { dg-do run { target native } } */
+
+#include <setjmp.h>
+jmp_buf buf;
+
+void noop () {}
+int identity (int x) { return x; }
+
+/* This function is a test to verify that the expression isolation does not
+ break on a CFG with the right set of complex edges. The (_ && setjmp)
+ created complex edges after the function calls and a circular pair of
+ complex edges around the setjmp call. This triggered a bug when the search
+ for right operands only would consider nodes dominated by the left-most
+ term, as this would only be the case if the complex edges were removed. (_
+ && setjmp) is undefined behavior, but it does happen in the wild.
+
+ __builtin_setjmp did not trigger this, so we need setjmp from libc. */
+void
+setjmp001 (int a, int b, int c)
+{
+ if (a) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ noop ();
+
+ if (b) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ noop ();
+
+ if (c && setjmp (buf)) /* conditions(1/4) true(0 1) false(1) */
+ /* conditions(end) */
+ noop ();
+}
+
+/* Adapted from freetype-2.13.0 gxvalid/gxvmod.c classic_kern_validate */
+int
+setjmp002 (int a)
+{
+ int error = identity(a);
+
+ if (error) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ goto Exit;
+
+ if (a+1) /* conditions(1/2) false(0) */
+ /* conditions(end) */
+ {
+ noop ();
+ if (setjmp (buf)) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ noop ();
+
+ if (error) /* conditions(1/2) true(0) */
+ /* conditions(end) */
+ noop ();
+ }
+
+ error--;
+
+Exit:
+ return error;
+}
+
+int
+setjmp003 (int a)
+{
+ /* || setjmp is undefined behavior, so the result here does not have to
+ make sense. It would be nice if the result is not something like 35/4
+ conditions covered. */
+ if (a || setjmp (buf)) /* conditions(suppress) */
+ /* conditions(end) */
+ a += 12;
+
+ return a;
+}
+
+jmp_buf dest;
+
+int
+setdest ()
+{
+ if (setjmp (dest)) /* conditions(2/2) */
+ return 1;
+ return 2;
+}
+
+void
+jump ()
+{
+ longjmp (dest, 1);
+}
+
+int
+main ()
+{
+ setjmp001 (0, 1, 0);
+ setjmp002 (0);
+ setjmp003 (0);
+ setdest ();
+ jump ();
+}
+
+/* { dg-final { run-gcov conditions { --conditions gcov-22.c } } } */
diff --git a/gcc/testsuite/gcc.misc-tests/gcov-23.c b/gcc/testsuite/gcc.misc-tests/gcov-23.c
new file mode 100644
index 0000000..72849d8
--- /dev/null
+++ b/gcc/testsuite/gcc.misc-tests/gcov-23.c
@@ -0,0 +1,361 @@
+/* { dg-options "-fcondition-coverage -ftest-coverage -O2 -c" } */
+
+#include <stdint.h>
+#include <limits.h>
+#include <setjmp.h>
+jmp_buf buf;
+
+int id (int);
+int idp (int *);
+int err;
+char c;
+
+/* 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 build. 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/4) true(0 1) false(0 1) */
+ /* 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;
+}
+
+/* Adapted from icu4c-73.1 common/ucase.cpp ucase_getCaseLocale (). */
+int
+mcdc003 (const char *locale)
+{
+ /* extern, so its effect won't be optimized out. */
+ c = *locale++;
+ if (c == 'z') /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ return 1;
+ }
+ else if (c >= 'a') /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ {
+ if (id (c)) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ c = *locale++;
+ }
+ else
+ {
+ if (c == 'T')
+ {
+ if (id (c)) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ c = *locale++;
+ if (id (c)) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ c = *locale++;
+ }
+ /* This may or may not become a jump table. */
+ else if (c == 'L') /* conditions(suppress) */
+ /* conditions(end) */
+ c = *locale++;
+ else if (c == 'E') /* conditions(suppress) */
+ /* conditions(end) */
+ c = *locale++;
+ else if (c == 'N') /* conditions(suppress) */
+ /* conditions(end) */
+ c = *locale++;
+ else if (c == 'H') /* conditions(suppress) */
+ /* conditions(end) */
+ {
+ c = *locale++;
+ if (id (c)) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ c = *locale++;
+ }
+ }
+
+ return 1;
+}
+
+/* The || will be changed to |, so it is impractical to predict the number of
+ conditions. If the walk is not properly implemented this will not finish
+ compiling, so the actual coverage is not interesting. */
+/* Adapted from icu4c-73.1 common/uresdata.cpp res_findResource (). */
+int
+mcdc004 (int r, char* path, char* key)
+{
+ char *idcc (char *, char);
+ #define is_kind1(type) ((type) == 23 || (type) == 14 || (type == 115))
+ #define is_kind2(type) ((type) == 16 || (type) == 77 || (type == 118))
+ #define is_kind12(type) (is_kind1 ((type)) || is_kind2 ((type)))
+
+ char *nextSepP = path;
+ int t1 = r;
+ int type = id (t1);
+
+ if (!is_kind12 (type)) /* conditions(suppress) */
+ /* conditions(end) */
+ return -1;
+
+ while (*path && t1 != -1 && is_kind12 (type)) /* conditions(suppress) */
+ /* conditions(end) */
+ {
+ nextSepP = idcc(path, '/');
+ if(nextSepP == path) /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ return -1;
+
+ if (*nextSepP == 'a') /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ *key = *path;
+ else if (*nextSepP == 'b') /* conditions(0/2) true(0) false(0) */
+ /* conditions(end) */
+ *key = 0;
+ type = t1;
+ }
+
+ return t1;
+}
+
+/* Adapted from jxl 0.8.2 lib/extras/dec/apng.cc processing_start ().
+ This created a graph where depth-first traversal of the CFG would not
+ process nodes in the wrong order (the extra control inserted from setjmp
+ created a path of complexes from root to !b without going through !a).
+
+ This only happened under optimization. */
+int
+mcdc005 (int a, int b)
+{
+ a = id (a);
+ b = id (b);
+
+ /* The a || b gets transformed to a | b, then fused with setjmp because
+ they both have the same return value. */
+ if (a || b) /* conditions(0/4) true(0 1) false(0 1) */
+ /* conditions(end) */
+ return 1;
+ else
+ a += 1;
+
+ if (setjmp (buf))
+ return 1;
+
+ return a;
+}
+
+/* Adapted from cpio-2.14 gnu/quotearg.c quotearg_buffer_restyled. The ifs in
+ the cases (with fallthrough) re-use the same value which under optimization
+ causes path reuse which must be sorted out. */
+int
+mcdc006 (int quoting_style, int elide, int *buffer)
+{
+ int backslash = 0;
+ switch (quoting_style)
+ {
+ case 1:
+ if (!elide)
+ backslash = 1;
+ case 2:
+ if (!elide)
+ if (buffer)
+ *buffer = '"';
+ }
+
+ if (quoting_style == 2 && backslash)
+ quoting_style = 1;
+ return 1;
+}
+
+/* Adapted from pcre2-10.42 pcre2_compile.c pcre2_compile. If SSA nodes are
+ created at the wrong place in the block it will fail flow analysis (because
+ the label is in the middle of block), caused by the final break in this
+ case. */
+void
+mcdc007 (int options, int *pso, int len)
+{
+ if (options == 5)
+ return;
+
+ while (options--)
+ {
+ int i;
+ for (i = 0; i < len; i++)
+ {
+ int *p = pso + i;
+ int skipatstart = *p + 2;
+ if (skipatstart) {
+ switch(*p)
+ {
+ case 1:
+ *p |= *p + 1;
+ break;
+ case 2:
+ skipatstart += *p - skipatstart;
+ break;
+ }
+ break;
+ }
+ }
+ if (i >= len) break;
+ }
+}
+
+/* Adapted from alsa-lib 1.2.8 pcm/pcm.c snd_pcm_chmap_print. */
+int
+mcdc008 (int *map, unsigned maxlen, int *buf)
+{
+ unsigned int len = 0;
+ for (unsigned i = 0; i < *map; i++) {
+ unsigned int p = map[i] & 0xF;
+ if (i > 0) {
+ if (len >= maxlen)
+ return -1;
+ }
+ if (map[i] & 0xFF)
+ len += idp (buf + len);
+ else {
+ len += idp (buf);
+ }
+ if (map[i] & 0xFF00) {
+ len += idp (buf + len);
+ if (len >= maxlen)
+ return -1;
+ }
+ }
+ return len;
+}
+
+/* Adapted from cpio-2.14 gnu/mktime.c mktime_internal (). The combination of
+ goto, automatic variables, and the ternary causes the post dominator of the
+ highest topological ordered node not to be the common post dominator of the
+ expression as a whole. */
+int
+mcdc009 (int *tp, int t, int isdst)
+{
+ int t0 = tp[0];
+ int t1 = tp[1];
+ int t2 = tp[2];
+
+ if (t0 < 0 || (isdst < 0 ? t1 : (isdst != 0)))
+ goto offset_found;
+
+ if (t == 0)
+ return -1;
+
+ t1 = t2;
+
+offset_found:
+ return t;
+}
+
+/* Adapted from Berkeley db 4.8.30 rep/rep_elect.c __rep_cmp_vote. This
+ particular combination of fallthrough and folding creates a path into the
+ the inner if () that does not go through the first basic condition. */
+void
+mcdc010 (int cmp, int *rep, int sites, int priority, int flags)
+{
+ if (sites > 1 && (priority != 0 || (flags & 0xFF)))
+ {
+ if ( (priority != 0 && *rep == 0)
+ || (((priority == 0 && *rep == 0)
+ || (priority != 0 && *rep != 0)) && cmp > 0))
+ {
+ *rep = cmp;
+ }
+ }
+}
+
+/* For not sufficiently protected back edges this would create an infinite
+ loop. */
+void
+mcdc011 (int a, int b)
+{
+ if (a && id (b))
+ for (;;) {}
+ id (a+1);
+}
+
+/* Adapted from alsa-1.2.8 tlv.c get_tlv_info (). Under optimization, the
+ conditions may be replaced with min (). */
+int
+mcdc012 (int x, int y)
+{
+ int err;
+ err = id (x);
+ if (err < 0)
+ return err;
+ err = id (y);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* Adapted from alsa-1.2.8 control.c snd_ctl_elem_id_compare_numid (). This
+ test is probably not so accurate on targets where int == int64. Under
+ optimization, the conditions may be replaced with min/max. */
+int
+mcdc013 (const int64_t *id1, const int64_t *id2)
+{
+ int64_t d;
+ d = *id1 - *id2;
+ if (d & 0xFF)
+ {
+ if (d > INT_MAX)
+ d = INT_MAX;
+ else if (d < INT_MIN)
+ d = INT_MIN;
+ }
+ return d;
+}
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index fe6cd96..dd47d66 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -175,6 +175,252 @@ proc verify-branches { testname testcase file } {
}
#
+# verify-conditions -- check that conditions are checked as expected
+#
+# TESTNAME is the name of the test, including unique flags.
+# TESTCASE is the name of the test file.
+# FILE is the name of the gcov output file.
+#
+# Checks are based on comments in the source file. Condition coverage comes
+# with with two types of output, a summary and a list of the uncovered
+# conditions. Both must be checked to pass the test
+#
+# To check for conditions, add a comment the line of a conditional:
+# /* conditions(n/m) true(0 1) false(1) */
+#
+# where n/m are the covered and total conditions in the expression. The true()
+# and false() take the indices expected *not* covered.
+#
+# This means that all coverage statements should have been seen:
+# /* conditions(end) */
+#
+# If all conditions are covered i.e. n == m, then conditions(end) can be
+# omitted. If either true() or false() are empty they can be omitted too.
+#
+# In some very specific cases there is a need to match multiple conditions on
+# the same line, for example if (a && fn (b || c) && d), which is interpreted
+# roughly as tmp _bc = b || c; if (a && _bc && d). The argument to fn is
+# considered its own expression and its coverage report will be written on the
+# same line. For these cases, use conditions(n/m; n/m;...) true(0 1;;...)
+# where ; marks the end of the list element where the ith list matches the ith
+# expression. The true()/false() matchers can be omitted if no expression
+# expects them, otherwise use the empty list if all true/false outcomes are
+# covered.
+#
+# C++ can insert conditionals in the CFG that are not present in source code.
+# These must be manually suppressed since unexpected and unhandled conditions
+# are an error (to help combat regressions). Output can be suppressed with
+# conditions(suppress) and conditions(end). suppress should usually be on a
+# closing brace.
+#
+# Some expressions, when using unnamed temporaries as operands, will have
+# destructors in expressions. The coverage of the destructor will be reported
+# on the same line as the expression itself, but suppress() would also swallow
+# the expected tested-for messages. To handle these, use the destructor() [1]
+# which will suppress everything from and including the second "conditions
+# covered".
+#
+# [1] it is important that the destructor() is *on the same line* as the
+# conditions(m/n)
+proc verify-conditions { testname testcase file } {
+ set failed 0
+ set suppress 0
+ set destructor 0
+ set should ""
+ set shouldt ""
+ set shouldf ""
+ set shouldall ""
+ set fd [open $file r]
+ set lineno 0
+ set checks [list]
+ set keywords {"end" "suppress"}
+ while {[gets $fd line] >= 0} {
+ regexp "^\[^:\]+: *(\[0-9\]+):" "$line" all lineno
+ set prefix "$testname line $lineno"
+
+ if {![regexp "condition" $line]} {
+ continue
+ }
+
+ # Missing coverage for both true and false will cause a failure, but
+ # only count it once for the report.
+ set ok 1
+ if [regexp {conditions *\([0-9a-z/; ]+\)} "$line" all] {
+ # *Very* coarse sanity check: conditions() should either be a
+ # keyword or n/m, anything else means a buggy test case. end is
+ # optional for cases where all conditions are covered, since it
+ # only expects a single line of output.
+ regexp {conditions *\(([0-9a-z/]+)\)} "$line" all e
+ if {([lsearch -exact $keywords $e] >= 0 || [regexp {\d+/\d+} "$e"]) == 0} {
+ fail "$prefix: expected conditions (n/m), (suppress) or (end); was ($e)"
+ incr failed
+ continue
+ }
+
+ # Any keyword means a new context. Set the error flag if not all
+ # expected output has been seen, and reset the state.
+ if {[llength $shouldt] != 0} {
+ fail "$prefix: expected 'not covered (true)' for terms: $shouldt"
+ set ok 0
+ }
+
+ if {[llength $shouldf] != 0} {
+ fail "$prefix: expected 'not covered (false)' for terms: $shouldf"
+ set ok 0
+ }
+
+ if {$shouldall ne ""} {
+ fail "$prefix: coverage summary not found; expected $shouldall"
+ set ok 0
+ }
+
+ if {[llength $checks] != 0} {
+ set missing [llength checks]
+ fail "$prefix: expected $missing more conditions"
+ set ok 0
+ }
+
+ set suppress 0
+ set destructor 0
+ set setup 0
+ set checks [list]
+
+ if [regexp {destructor\(\)} "$line"] {
+ set destructor 1
+ }
+
+ # Find the expressions on this line. There may be more, to support
+ # constructs like (a && fn (b && c) && d).
+ # The match produces lists like [conditions(n/m) n m]
+ set argconds ""
+ set argtrue ""
+ set argfalse ""
+ regexp {conditions *\(([0-9 /;]+)\)} $line _ argconds
+ regexp {true *\(([0-9 ;]+)\)} $line _ argtrue
+ regexp {false *\(([0-9 ;]+)\)} $line _ argfalse
+ set condv [split $argconds ";"]
+ set truev [split $argtrue ";"]
+ set falsev [split $argfalse ";"]
+ set ncases [llength $condv]
+
+ for {set i 0} {$i < $ncases} {incr i} {
+ set summary [lindex $condv $i]
+ set n [lindex [split $summary "/"] 0]
+ set m [lindex [split $summary "/"] 1]
+ set newt [lindex $truev $i]
+ set newf [lindex $falsev $i]
+
+ # Sanity check - if the true() and false() vectors should have
+ # m-n elements to cover all uncovered conditions. Because of
+ # masking it can sometimes be surprising what terms are
+ # independent, so this makes for more robust test at the cost
+ # of being slightly more annoying to write.
+ set nterms [expr [llength $newt] + [llength $newf]]
+ set nexpected [expr {$m - $n}]
+ if {$nterms != $nexpected} {
+ fail "$prefix: expected $nexpected uncovered terms; got $nterms"
+ set ok 0
+ }
+ set shouldall $e
+ set should ""
+ set shouldt $newt
+ set shouldf $newf
+ set shouldall [regsub -all { } "$n/$m" ""]
+ lappend checks [list $should $shouldt $shouldf $shouldall $newt $newf]
+ }
+
+ if {[llength $checks] > 0} {
+ # no-op - the stack of checks to do is set up
+ } elseif {$e == "end"} {
+ # no-op - state should already been reset, and errors flagged
+ } elseif {$e == "suppress"} {
+ set suppress 1
+ } else {
+ # this should be unreachable,
+ fail "$prefix: unhandled control ($e), should be unreachable"
+ set ok 0
+ }
+ } elseif {$suppress == 1} {
+ # ignore everything in a suppress block. C++ especially can insert
+ # conditionals in exceptions and destructors which would otherwise
+ # be considered unhandled.
+ continue
+ } elseif [regexp {condition +(\d+) not covered \((.*)\)} "$line" all cond condv] {
+ foreach v {true false} {
+ if [regexp $v $condv] {
+ if {"$v" == "true"} {
+ set should shouldt
+ } else {
+ set should shouldf
+ }
+
+ set i [lsearch [set $should] $cond]
+ if {$i != -1} {
+ set $should [lreplace [set $should] $i $i]
+ } else {
+ fail "$prefix: unexpected uncovered term $cond ($v)"
+ set ok 0
+ }
+ }
+ }
+ } elseif [regexp {condition outcomes covered (\d+/\d+)} "$line" all cond] {
+ # the destructor-generated "conditions covered" lines will be
+ # written after all expression-related output. Handle these by
+ # turning on suppression if the destructor-suppression is
+ # requested.
+ if {$shouldall == "" && $destructor == 1} {
+ set suppress 1
+ continue
+ }
+
+ if {[llength $checks] == 0} {
+ fail "$prefix: unexpected summary $cond"
+ set ok 0
+ } else {
+ # Report any missing conditions from the previous set if this
+ # is not the first condition block
+ if {$setup == 1} {
+ if {[llength $shouldt] != 0} {
+ fail "$prefix: expected 'not covered (true)' for terms: $shouldt"
+ set ok 0
+ }
+ if {[llength $shouldf] != 0} {
+ fail "$prefix: expected 'not covered (false)' for terms: $shouldf"
+ set ok 0
+ }
+ if {$shouldall ne ""} {
+ fail "$prefix: coverage summary not found; expected $shouldall"
+ set ok 0
+ }
+ }
+ set setup 1
+ set current [lindex $checks 0]
+ set checks [lreplace $checks 0 0]
+ set should [lindex $current 0]
+ set shouldt [lindex $current 1]
+ set shouldf [lindex $current 2]
+ set shouldall [lindex $current 3]
+ set newt [lindex $current 4]
+ set newf [lindex $current 5]
+
+ if {$cond == $shouldall} {
+ set shouldall ""
+ } else {
+ fail "$prefix: unexpected summary - expected $shouldall, got $cond"
+ set ok 0
+ }
+ }
+ }
+
+ if {$ok != 1} {
+ incr failed
+ }
+ }
+ close $fd
+ return $failed
+}
+
+#
# verify-calls -- check that call return percentages are as expected
#
# TESTNAME is the name of the test, including unique flags.
@@ -321,6 +567,7 @@ proc run-gcov { args } {
set gcov_args ""
set gcov_verify_calls 0
set gcov_verify_branches 0
+ set gcov_verify_conditions 0
set gcov_verify_lines 1
set gcov_verify_intermediate 0
set gcov_remove_gcda 0
@@ -331,10 +578,13 @@ proc run-gcov { args } {
set gcov_verify_calls 1
} elseif { $a == "branches" } {
set gcov_verify_branches 1
+ } elseif { $a == "conditions" } {
+ set gcov_verify_conditions 1
} elseif { $a == "intermediate" } {
set gcov_verify_intermediate 1
set gcov_verify_calls 0
set gcov_verify_branches 0
+ set gcov_verify_conditions 0
set gcov_verify_lines 0
} elseif { $a == "remove-gcda" } {
set gcov_remove_gcda 1
@@ -404,6 +654,11 @@ proc run-gcov { args } {
} else {
set bfailed 0
}
+ if { $gcov_verify_conditions } {
+ set cdfailed [verify-conditions $testname $testcase $testcase.gcov]
+ } else {
+ set cdfailed 0
+ }
if { $gcov_verify_calls } {
set cfailed [verify-calls $testname $testcase $testcase.gcov]
} else {
@@ -418,12 +673,12 @@ proc run-gcov { args } {
# Report whether the gcov test passed or failed. If there were
# multiple failures then the message is a summary.
- set tfailed [expr $lfailed + $bfailed + $cfailed + $ifailed]
+ set tfailed [expr $lfailed + $bfailed + $cdfailed + $cfailed + $ifailed]
if { $xfailed } {
setup_xfail "*-*-*"
}
if { $tfailed > 0 } {
- fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cfailed in return percentages, $ifailed in intermediate format"
+ fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cdfailed in condition/decision, $cfailed in return percentages, $ifailed in intermediate format"
if { $xfailed } {
clean-gcov $testcase
}