public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: "Jørgen Kvalsvik" <j@lambda.is>
To: gcc-patches@gcc.gnu.org
Cc: richard.guenther@gmail.com, jh@suse.cz, Jan Hubicka <hubicka@ucw.cz>
Subject: Ping^5 [PATCH v9 1/2] Add condition coverage (MC/DC)
Date: Wed, 21 Feb 2024 20:08:54 +0100	[thread overview]
Message-ID: <31fa5f1a-236e-4b40-a2e4-3fe5c36a1b3d@lambda.is> (raw)
In-Reply-To: <15dce852-1419-49bd-89f4-dc6cfffab49f@lambda.is>

On 06/02/2024 14:09, Jørgen Kvalsvik wrote:
> On 29/01/2024 09:53, Jørgen Kvalsvik wrote:
>> On 22/01/2024 11:21, Jørgen Kvalsvik wrote:
>>> On 09/01/2024 10:04, Jørgen Kvalsvik wrote:
>>>> On 02/01/2024 22:07, Jørgen Kvalsvik wrote:
>>>>> On 31/12/2023 16:51, Jørgen Kvalsvik wrote:
>>>>>> 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 flavours, 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 determining masking from an AST 
>>>>>> walk,
>>>>>> but my algorithm figures this out from analyzing the control flow 
>>>>>> graph.
>>>>>> The CFG is considered a binary decision diagram and an evaluation
>>>>>> becomes a path through the BDD, which is recorded. Certain paths will
>>>>>> mask ("null out") the contribution from earlier path segments, 
>>>>>> which can
>>>>>> be determined by finding short circuit endpoints. Masking is the
>>>>>> short circuiting of terms in the reverse-ordered Boolean function, 
>>>>>> and
>>>>>> the masked terms do not affect the decision like short-circuited
>>>>>> terms 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. This takes only 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, not the algorithm, so support 
>>>>>> for more
>>>>>> conditions can be added by also 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 (allocate_struct_function): Init 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.
>>>>>>     (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.
>>>>>> ---
>>>>>>   gcc/builtins.cc                        |    2 +-
>>>>>>   gcc/collect2.cc                        |    7 +-
>>>>>>   gcc/common.opt                         |    9 +
>>>>>>   gcc/doc/gcov.texi                      |   38 +
>>>>>>   gcc/doc/invoke.texi                    |   21 +
>>>>>>   gcc/function.cc                        |    1 +
>>>>>>   gcc/function.h                         |    4 +
>>>>>>   gcc/gcc.cc                             |    4 +-
>>>>>>   gcc/gcov-counter.def                   |    3 +
>>>>>>   gcc/gcov-dump.cc                       |   24 +
>>>>>>   gcc/gcov-io.h                          |    3 +
>>>>>>   gcc/gcov.cc                            |  209 ++-
>>>>>>   gcc/gimplify.cc                        |   94 +-
>>>>>>   gcc/ipa-inline.cc                      |    2 +-
>>>>>>   gcc/ipa-split.cc                       |    2 +-
>>>>>>   gcc/passes.cc                          |    3 +-
>>>>>>   gcc/profile.cc                         |   76 +-
>>>>>>   gcc/testsuite/g++.dg/gcov/gcov-18.C    |  258 ++++
>>>>>>   gcc/testsuite/gcc.misc-tests/gcov-19.c | 1737 
>>>>>> ++++++++++++++++++++++++
>>>>>>   gcc/testsuite/gcc.misc-tests/gcov-20.c |   22 +
>>>>>>   gcc/testsuite/gcc.misc-tests/gcov-21.c |   16 +
>>>>>>   gcc/testsuite/gcc.misc-tests/gcov-22.c |  103 ++
>>>>>>   gcc/testsuite/gcc.misc-tests/gcov-23.c |  361 +++++
>>>>>>   gcc/testsuite/lib/gcov.exp             |  259 +++-
>>>>>>   gcc/tree-core.h                        |    3 +
>>>>>>   gcc/tree-profile.cc                    | 1057 +++++++++++++-
>>>>>>   gcc/tree.h                             |    4 +
>>>>>>   libgcc/libgcov-merge.c                 |    5 +
>>>>>>   28 files changed, 4287 insertions(+), 40 deletions(-)
>>>>>>   create mode 100644 gcc/testsuite/g++.dg/gcov/gcov-18.C
>>>>>>   create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-19.c
>>>>>>   create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-20.c
>>>>>>   create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-21.c
>>>>>>   create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-22.c
>>>>>>   create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-23.c
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> Changes since v8:
>>>>>>
>>>>>> * Annotation uses a hash map instead of (ab)using the gimple.uid 
>>>>>> field.
>>>>>> * Minor documentation updates
>>>>>>
>>>>>> So the problems I had with the gc must have been other problems 
>>>>>> with my
>>>>>> implementation, as the approach itself is fine. It seems like these
>>>>>> problems are gone with this patch, which again uses a hash map in 
>>>>>> struct
>>>>>> function to map gcond -> uid.
>>>>>
>>>>> So it turns out I did not test this well enough, and the gc issues 
>>>>> were still there. The arm/arm64 CI runs fails on 
>>>>> libgomp.c++/member-2.C, libgomp.c++/depend-iterator-1.C, and I 
>>>>> could trigger a failure on my linux-x86_64 on libgomp.c++/for-23.C.
>>>>>
>>>>> My best guess is that the omp pass deletes some gcond statement 
>>>>> that the hash map later tries to mark for gc, as it fails in a gc 
>>>>> run. Since the gcond->uid mapping is only queried based on live 
>>>>> objects and is non-owning, making it use a cache-type map seems to 
>>>>> solve the problem, like in this patch:
>>>>>
>>>>> diff --git a/gcc/function.cc b/gcc/function.cc
>>>>> index e57d0488c7a..452c456dca0 100644
>>>>> --- a/gcc/function.cc
>>>>> +++ b/gcc/function.cc
>>>>> @@ -4793,7 +4793,8 @@ allocate_struct_function (tree fndecl, bool 
>>>>> abstract_p)
>>>>>
>>>>>     cfun = ggc_cleared_alloc<function> ();
>>>>>
>>>>> -  cfun->cond_uids = hash_map <gcond*, unsigned>::create_ggc ();
>>>>> +  cfun->cond_uids = hash_map <gcond*, unsigned, 
>>>>> gcond_cache_map_traits>
>>>>> +                   ::create_ggc ();
>>>>>     init_eh_for_function ();
>>>>>
>>>>>     if (init_machine_status)
>>>>> diff --git a/gcc/function.h b/gcc/function.h
>>>>> index a3a23108ee9..31d56856048 100644
>>>>> --- a/gcc/function.h
>>>>> +++ b/gcc/function.h
>>>>> @@ -243,6 +243,16 @@ public:
>>>>>     (current_function_dynamic_stack_size != 0               \
>>>>>      || current_function_has_unbounded_dynamic_stack_size)
>>>>>
>>>>> +/* Traits for a gcond -> unsigned hash map grouping basic 
>>>>> conditions broken down
>>>>> +   from ANDIF/ORIF expressions together, used for condition 
>>>>> coverage. This is
>>>>> +   a cache/view as gcond statements may be deleted by other passes 
>>>>> causing
>>>>> +   double frees in gc runs.  Stale entries are not a problem 
>>>>> because only live
>>>>> +   entries can be looked up.  */
>>>>> +struct gcond_cache_map_traits
>>>>> +: simple_cache_map_traits <ggc_cache_ptr_hash <gcond>, unsigned>
>>>>> +{
>>>>> +};
>>>>> +
>>>>>   /* This structure can save all the important global and static 
>>>>> variables
>>>>>      describing the status of the current function.  */
>>>>>
>>>>> @@ -270,9 +280,9 @@ struct GTY(()) function {
>>>>>     /* Value histograms attached to particular statements.  */
>>>>>     htab_t GTY((skip)) value_histograms;
>>>>>
>>>>> -  /* Annotated gconds so that basic conditions in the same 
>>>>> expression have the
>>>>> -     same uid.  This is used for condition coverage.  */
>>>>> -  hash_map <gcond*, unsigned> *GTY(()) cond_uids;
>>>>> +  /* Annotated gconds so that basic conditions in the same 
>>>>> expression map to
>>>>> +     the same same uid.  This is used for condition coverage.  */
>>>>> +  hash_map <gcond*, unsigned, gcond_cache_map_traits> *GTY(()) 
>>>>> cond_uids;
>>>>>
>>>>>     /* For function.cc.  */
>>>>>
>>>>>
>>>>> ---
>>>>>
>>>>> If you have a better idea I would be happy to implement it.
>>>>>
>>>>> Here is an ICU trace from the test failure on my machine. Do you 
>>>>> spot anything odd or disproving?
>>>>>
>>>>> gcc/libgomp/testsuite/libgomp.c++/for-23.C: In function 
>>>>> '_Z2f71JI1LIiEE._omp_fn.0':
>>>>> gcc/libgomp/testsuite/libgomp.c++/for-23.C:212:9: internal compiler 
>>>>> error: Segmentation fault
>>>>> 0x1319cff crash_signal
>>>>>          ../../gcc/toplev.cc:316
>>>>> 0x7fbb8d2fcd5f ???
>>>>>          ./signal/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0
>>>>> 0xd96f03 lookup_page_table_entry
>>>>>          ../../gcc/ggc-page.cc:630
>>>>> 0xd96f03 ggc_set_mark(void const*)
>>>>>          ../../gcc/ggc-page.cc:1550
>>>>> 0x102010b gt_ggc_mx_vec_edge_va_gc_(void*)
>>>>>          gcc/gtype-desc.cc:2529
>>>>> 0x1021cf4 gt_ggc_mx_vec_edge_va_gc_(void*)
>>>>>          gcc/gtype-desc.cc:1569
>>>>> 0x1021cf4 gt_ggc_mx_basic_block_def(void*)
>>>>>          gcc/gtype-desc.cc:1560
>>>>> 0x1024857 gt_ggc_mx_gimple(void*)
>>>>>          gcc/gtype-desc.cc:1306
>>>>> 0x1024d48 gt_ggc_mx(gcond*&)
>>>>>          gcc/gtype-desc.cc:2303
>>>>> 0x1024d48 hash_map<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> 
>>>>>  >::hash_entry::ggc_mx(hash_map<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> 
>>>>>  >::hash_entry&)
>>>>>          ../../gcc/hash-map.h:75
>>>>> 0x1024d48 hash_map<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> 
>>>>>  >::hash_entry::ggc_maybe_mx(hash_map<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> 
>>>>>  >::hash_entry&)
>>>>>          ../../gcc/hash-map.h:82
>>>>> 0x1024d48 void gt_ggc_mx<hash_map<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> 
>>>>>  >::hash_entry>(hash_table<hash_map<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> 
>>>>>  >::hash_entry, false, xcallocator>*)
>>>>>          ../../gcc/hash-table.h:1255
>>>>> 0x1024d48 void gt_ggc_mx<hash_map<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> 
>>>>>  >::hash_entry>(hash_table<hash_map<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> 
>>>>>  >::hash_entry, false, xcallocator>*)
>>>>>          ../../gcc/hash-table.h:1240
>>>>> 0x1024d48 void gt_ggc_mx<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> 
>>>>>  >(hash_map<gcond*, unsigned int, 
>>>>> simple_hashmap_traits<default_hash_traits<gcond*>, unsigned int> >*)
>>>>>          ../../gcc/hash-map.h:321
>>>>> 0x1024d48 gt_ggc_mx_hash_map_gcond__unsigned_(void*)
>>>>>          gcc/gtype-desc.cc:2295
>>>>> 0x1028124 gt_ggc_mx_hash_map_gcond__unsigned_(void*)
>>>>>          gcc/gtype-desc.cc:1721
>>>>> 0x1028124 gt_ggc_mx_function(void*)
>>>>>          gcc/gtype-desc.cc:1711
>>>>> 0x1028124 gt_ggc_mx_function(void*)
>>>>>          gcc/gtype-desc.cc:1699
>>>>> 0xcbf058 gt_ggc_mx_lang_tree_node(void*)
>>>>>          ./gt-cp-tree.h:347
>>>>> 0xcbeb8b gt_ggc_mx_lang_tree_node(void*)
>>>>>          ./gt-cp-tree.h:472
>>>>>
>>>>> Thanks,
>>>>> Jørgen
>>>>>
>>>>
>>>> Ping. What do you think of this approach?
> 
> Ping. What do you think, and is there anything I can do to make this ready?
> 
> Thanks,
> Jørgen
> 
>>>>
>>>>
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> diff --git a/gcc/builtins.cc b/gcc/builtins.cc
>>>>>> index aa86ac1545d..f2a044bbbfa 100644
>>>>>> --- a/gcc/builtins.cc
>>>>>> +++ b/gcc/builtins.cc
>>>>>> @@ -5994,7 +5994,7 @@ expand_builtin_fork_or_exec (tree fn, tree 
>>>>>> exp, rtx target, int ignore)
>>>>>>     tree call;
>>>>>>     /* If we are not profiling, just call the function.  */
>>>>>> -  if (!profile_arc_flag)
>>>>>> +  if (!profile_arc_flag && !condition_coverage_flag)
>>>>>>       return NULL_RTX;
>>>>>>     /* Otherwise call the wrapper.  This should be equivalent for 
>>>>>> the rest of
>>>>>> diff --git a/gcc/collect2.cc b/gcc/collect2.cc
>>>>>> index c943f9f577c..af920ff2033 100644
>>>>>> --- a/gcc/collect2.cc
>>>>>> +++ b/gcc/collect2.cc
>>>>>> @@ -1035,9 +1035,9 @@ main (int argc, char **argv)
>>>>>>         lto_mode = LTO_MODE_LTO;
>>>>>>     }
>>>>>> -  /* -fno-profile-arcs -fno-test-coverage -fno-branch-probabilities
>>>>>> -     -fno-exceptions -w -fno-whole-program */
>>>>>> -  num_c_args += 6;
>>>>>> +  /* -fno-profile-arcs -fno-condition-coverage -fno-test-coverage
>>>>>> +     -fno-branch-probabilities -fno-exceptions -w 
>>>>>> -fno-whole-program */
>>>>>> +  num_c_args += 7;
>>>>>>     c_argv = XCNEWVEC (char *, num_c_args);
>>>>>>     c_ptr = CONST_CAST2 (const char **, char **, c_argv);
>>>>>> @@ -1233,6 +1233,7 @@ main (int argc, char **argv)
>>>>>>       }
>>>>>>     obstack_free (&temporary_obstack, temporary_firstobj);
>>>>>>     *c_ptr++ = "-fno-profile-arcs";
>>>>>> +  *c_ptr++ = "-fno-condition-coverage";
>>>>>>     *c_ptr++ = "-fno-test-coverage";
>>>>>>     *c_ptr++ = "-fno-branch-probabilities";
>>>>>>     *c_ptr++ = "-fno-exceptions";
>>>>>> diff --git a/gcc/common.opt b/gcc/common.opt
>>>>>> index 161a035d736..b93850b48dd 100644
>>>>>> --- a/gcc/common.opt
>>>>>> +++ b/gcc/common.opt
>>>>>> @@ -870,6 +870,11 @@ Wcoverage-invalid-line-number
>>>>>>   Common Var(warn_coverage_invalid_linenum) Init(1) Warning
>>>>>>   Warn in case a function ends earlier than it begins due to an 
>>>>>> invalid linenum macros.
>>>>>> +Wcoverage-too-many-conditions
>>>>>> +Common Var(warn_too_many_conditions) Init(1) Warning
>>>>>> +Warn when a conditional has too many terms and condition coverage 
>>>>>> profiling
>>>>>> +gives up instrumenting the expression.
>>>>>> +
>>>>>>   Wmissing-profile
>>>>>>   Common Var(warn_missing_profile) Init(1) Warning
>>>>>>   Warn in case profiles in -fprofile-use do not exist.
>>>>>> @@ -2458,6 +2463,10 @@ fprofile-arcs
>>>>>>   Common Var(profile_arc_flag)
>>>>>>   Insert arc-based program profiling code.
>>>>>> +fcondition-coverage
>>>>>> +Common Var(condition_coverage_flag)
>>>>>> +Insert condition coverage profiling code.
>>>>>> +
>>>>>>   fprofile-dir=
>>>>>>   Common Joined RejectNegative Var(profile_data_prefix)
>>>>>>   Set the top-level directory for storing the profile data.
>>>>>> diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
>>>>>> index 3019efc4674..6bd75959e94 100644
>>>>>> --- a/gcc/doc/gcov.texi
>>>>>> +++ b/gcc/doc/gcov.texi
>>>>>> @@ -124,6 +124,7 @@ gcov [@option{-v}|@option{--version}] 
>>>>>> [@option{-h}|@option{--help}]
>>>>>>        [@option{-a}|@option{--all-blocks}]
>>>>>>        [@option{-b}|@option{--branch-probabilities}]
>>>>>>        [@option{-c}|@option{--branch-counts}]
>>>>>> +     [@option{-g}|@option{--conditions}]
>>>>>>        [@option{-d}|@option{--display-progress}]
>>>>>>        [@option{-f}|@option{--function-summaries}]
>>>>>>        [@option{-j}|@option{--json-format}]
>>>>>> @@ -169,6 +170,14 @@ be shown, unless the @option{-u} option is 
>>>>>> given.
>>>>>>   Write branch frequencies as the number of branches taken, rather 
>>>>>> than
>>>>>>   the percentage of branches taken.
>>>>>> +@item -g
>>>>>> +@itemx --conditions
>>>>>> +Write condition coverage to the output file, and write condition 
>>>>>> summary info
>>>>>> +to the standard output.  This option allows you to see if the 
>>>>>> conditions in
>>>>>> +your program at least once had an independent effect on the 
>>>>>> outcome of the
>>>>>> +boolean expression (modified condition/decision coverage).  This 
>>>>>> requires you
>>>>>> +to compile the source with @option{-fcondition-coverage}.
>>>>>> +
>>>>>>   @item -d
>>>>>>   @itemx --display-progress
>>>>>>   Display the progress on the standard output.
>>>>>> @@ -301,6 +310,7 @@ Each @var{line} has the following form:
>>>>>>     "branches": ["$branch"],
>>>>>>     "calls": ["$call"],
>>>>>>     "count": 2,
>>>>>> +  "conditions": ["$condition"],
>>>>>>     "line_number": 15,
>>>>>>     "unexecuted_block": false,
>>>>>>     "function_name": "foo",
>>>>>> @@ -384,6 +394,34 @@ to @var{line::count})
>>>>>>   @var{destination_block_id}: ID of the basic block this calls 
>>>>>> continues after return
>>>>>>   @end itemize
>>>>>> +Each @var{condition} has the following form:
>>>>>> +
>>>>>> +@smallexample
>>>>>> +@{
>>>>>> +  "count": 4,
>>>>>> +  "covered": 2,
>>>>>> +  "not_covered_false": [],
>>>>>> +  "not_covered_true": [0, 1],
>>>>>> +@}
>>>>>> +
>>>>>> +@end smallexample
>>>>>> +
>>>>>> +Fields of the @var{condition} element have following semantics:
>>>>>> +
>>>>>> +@itemize @bullet
>>>>>> +@item
>>>>>> +@var{count}: number of condition outcomes in this expression
>>>>>> +
>>>>>> +@item
>>>>>> +@var{covered}: number of covered condition outcomes in this 
>>>>>> expression
>>>>>> +
>>>>>> +@item
>>>>>> +@var{not_covered_true}: terms, by index, not seen as true in this 
>>>>>> expression
>>>>>> +
>>>>>> +@item
>>>>>> +@var{not_covered_false}: terms, by index, not seen as false in 
>>>>>> this expression
>>>>>> +@end itemize
>>>>>> +
>>>>>>   @item -H
>>>>>>   @itemx --human-readable
>>>>>>   Write counts in human readable format (like 24.6k).
>>>>>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>>>>>> index f93128dbe2b..7bfd2478555 100644
>>>>>> --- a/gcc/doc/invoke.texi
>>>>>> +++ b/gcc/doc/invoke.texi
>>>>>> @@ -635,6 +635,7 @@ Objective-C and Objective-C++ Dialects}.
>>>>>>   @item Program Instrumentation Options
>>>>>>   @xref{Instrumentation Options,,Program Instrumentation Options}.
>>>>>>   @gccoptlist{-p  -pg  -fprofile-arcs  --coverage  -ftest-coverage
>>>>>> +-fcondition-coverage
>>>>>>   -fprofile-abs-path
>>>>>>   -fprofile-dir=@var{path}  -fprofile-generate 
>>>>>> -fprofile-generate=@var{path}
>>>>>>   -fprofile-info-section  -fprofile-info-section=@var{name}
>>>>>> @@ -6547,6 +6548,14 @@ poorly optimized code and is useful only in 
>>>>>> the
>>>>>>   case of very minor changes such as bug fixes to an existing 
>>>>>> code-base.
>>>>>>   Completely disabling the warning is not recommended.
>>>>>> +@opindex Wno-coverage-too-many-conditions
>>>>>> +@opindex Wcoverage-too-many-conditions
>>>>>> +@item -Wno-coverage-too-many-conditions
>>>>>> +Warn if @option{-fcondition-coverage} is used and an expression 
>>>>>> have too many
>>>>>> +terms and GCC gives up coverage.  Coverage is given up when there 
>>>>>> are more
>>>>>> +terms in the conditional than there are bits in a 
>>>>>> @code{gcov_type_unsigned}.
>>>>>> +This warning is enabled by default.
>>>>>> +
>>>>>>   @opindex Wno-coverage-invalid-line-number
>>>>>>   @opindex Wcoverage-invalid-line-number
>>>>>>   @item -Wno-coverage-invalid-line-number
>>>>>> @@ -16867,6 +16876,14 @@ Note that if a command line directly 
>>>>>> links source files, the corresponding
>>>>>>   E.g. @code{gcc a.c b.c -o binary} would generate 
>>>>>> @file{binary-a.gcda} and
>>>>>>   @file{binary-b.gcda} files.
>>>>>> +@item -fcondition-coverage
>>>>>> +@opindex fcondition-coverage
>>>>>> +Add code so that program conditions are instrumented.  During 
>>>>>> execution the
>>>>>> +program records what terms in a conditional contributes to a 
>>>>>> decision, which
>>>>>> +can be used to verify that all terms in a Boolean function are 
>>>>>> tested and have
>>>>>> +an independent effect on the outcome of a decision.  The result 
>>>>>> can be read
>>>>>> +with @code{gcov --conditions}.
>>>>>> +
>>>>>>   @xref{Cross-profiling}.
>>>>>>   @cindex @command{gcov}
>>>>>> @@ -16929,6 +16946,10 @@ executed.  When an arc is the only exit 
>>>>>> or only entrance to a block, the
>>>>>>   instrumentation code can be added to the block; otherwise, a new 
>>>>>> basic
>>>>>>   block must be created to hold the instrumentation code.
>>>>>> +With @option{-fcondition-coverage}, for each conditional in your 
>>>>>> program GCC
>>>>>> +creates a bitset and records the exercised boolean values that 
>>>>>> have an
>>>>>> +independent effect on the outcome of that expression.
>>>>>> +
>>>>>>   @need 2000
>>>>>>   @opindex ftest-coverage
>>>>>>   @item -ftest-coverage
>>>>>> diff --git a/gcc/function.cc b/gcc/function.cc
>>>>>> index 89841787ff8..e57d0488c7a 100644
>>>>>> --- a/gcc/function.cc
>>>>>> +++ b/gcc/function.cc
>>>>>> @@ -4793,6 +4793,7 @@ allocate_struct_function (tree fndecl, bool 
>>>>>> abstract_p)
>>>>>>     cfun = ggc_cleared_alloc<function> ();
>>>>>> +  cfun->cond_uids = hash_map <gcond*, unsigned>::create_ggc ();
>>>>>>     init_eh_for_function ();
>>>>>>     if (init_machine_status)
>>>>>> diff --git a/gcc/function.h b/gcc/function.h
>>>>>> index 833c35e3da6..a3a23108ee9 100644
>>>>>> --- a/gcc/function.h
>>>>>> +++ b/gcc/function.h
>>>>>> @@ -270,6 +270,10 @@ struct GTY(()) function {
>>>>>>     /* Value histograms attached to particular statements.  */
>>>>>>     htab_t GTY((skip)) value_histograms;
>>>>>> +  /* Annotated gconds so that basic conditions in the same 
>>>>>> expression have the
>>>>>> +     same uid.  This is used for condition coverage.  */
>>>>>> +  hash_map <gcond*, unsigned> *GTY(()) cond_uids;
>>>>>> +
>>>>>>     /* For function.cc.  */
>>>>>>     /* Points to the FUNCTION_DECL of this function.  */
>>>>>> diff --git a/gcc/gcc.cc b/gcc/gcc.cc
>>>>>> index 9f21ad9453e..8578abc84f7 100644
>>>>>> --- a/gcc/gcc.cc
>>>>>> +++ b/gcc/gcc.cc
>>>>>> @@ -1165,7 +1165,7 @@ proper position among the other output 
>>>>>> files.  */
>>>>>>       %:include(libgomp.spec)%(link_gomp)}\
>>>>>>       %{fgnu-tm:%:include(libitm.spec)%(link_itm)}\
>>>>>>       " STACK_SPLIT_SPEC "\
>>>>>> -    %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} " 
>>>>>> SANITIZER_SPEC " \
>>>>>> + 
>>>>>> %{fprofile-arcs|fcondition-coverage|fprofile-generate*|coverage:-lgcov} " SANITIZER_SPEC " \
>>>>>>       %{!nostdlib:%{!r:%{!nodefaultlibs:%(link_ssp) 
>>>>>> %(link_gcc_c_sequence)}}}\
>>>>>>       %{!nostdlib:%{!r:%{!nostartfiles:%E}}} %{T*}  \n%(post_link) 
>>>>>> }}}}}}"
>>>>>>   #endif
>>>>>> @@ -1288,7 +1288,7 @@ static const char *cc1_options =
>>>>>>    %{!fsyntax-only:%{S:%W{o*}%{!o*:-o %w%b.s}}}\
>>>>>>    %{fsyntax-only:-o %j} %{-param*}\
>>>>>>    %{coverage:-fprofile-arcs -ftest-coverage}\
>>>>>> - %{fprofile-arcs|fprofile-generate*|coverage:\
>>>>>> + %{fprofile-arcs|fcondition-coverage|fprofile-generate*|coverage:\
>>>>>>      %{!fprofile-update=single:\
>>>>>>        %{pthread:-fprofile-update=prefer-atomic}}}";
>>>>>> diff --git a/gcc/gcov-counter.def b/gcc/gcov-counter.def
>>>>>> index 727ef424181..4d5b9c65a70 100644
>>>>>> --- a/gcc/gcov-counter.def
>>>>>> +++ b/gcc/gcov-counter.def
>>>>>> @@ -49,3 +49,6 @@ DEF_GCOV_COUNTER(GCOV_COUNTER_IOR, "ior", _ior)
>>>>>>   /* Time profile collecting first run of a function */
>>>>>>   DEF_GCOV_COUNTER(GCOV_TIME_PROFILER, "time_profiler", 
>>>>>> _time_profile)
>>>>>> +
>>>>>> +/* Conditions.  The counter is interpreted as a bit-set.  */
>>>>>> +DEF_GCOV_COUNTER(GCOV_COUNTER_CONDS, "conditions", _ior)
>>>>>> diff --git a/gcc/gcov-dump.cc b/gcc/gcov-dump.cc
>>>>>> index 20e464022dc..d843223986c 100644
>>>>>> --- a/gcc/gcov-dump.cc
>>>>>> +++ b/gcc/gcov-dump.cc
>>>>>> @@ -38,6 +38,7 @@ static void print_version (void);
>>>>>>   static void tag_function (const char *, unsigned, int, unsigned);
>>>>>>   static void tag_blocks (const char *, unsigned, int, unsigned);
>>>>>>   static void tag_arcs (const char *, unsigned, int, unsigned);
>>>>>> +static void tag_conditions (const char *, unsigned, int, unsigned);
>>>>>>   static void tag_lines (const char *, unsigned, int, unsigned);
>>>>>>   static void tag_counters (const char *, unsigned, int, unsigned);
>>>>>>   static void tag_summary (const char *, unsigned, int, unsigned);
>>>>>> @@ -77,6 +78,7 @@ static const tag_format_t tag_table[] =
>>>>>>     {GCOV_TAG_FUNCTION, "FUNCTION", tag_function},
>>>>>>     {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks},
>>>>>>     {GCOV_TAG_ARCS, "ARCS", tag_arcs},
>>>>>> +  {GCOV_TAG_CONDS, "CONDITIONS", tag_conditions},
>>>>>>     {GCOV_TAG_LINES, "LINES", tag_lines},
>>>>>>     {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>>>>>     {0, NULL, NULL}
>>>>>> @@ -392,6 +394,28 @@ tag_arcs (const char *filename ATTRIBUTE_UNUSED,
>>>>>>       }
>>>>>>   }
>>>>>> +/* Print number of conditions (not outcomes, i.e. if (x && y) is 
>>>>>> 2, not 4).  */
>>>>>> +static void
>>>>>> +tag_conditions (const char *filename, unsigned /* tag */, int 
>>>>>> length,
>>>>>> +        unsigned depth)
>>>>>> +{
>>>>>> +  unsigned n_conditions = GCOV_TAG_CONDS_NUM (length);
>>>>>> +
>>>>>> +  printf (" %u conditions", n_conditions);
>>>>>> +  if (flag_dump_contents)
>>>>>> +    {
>>>>>> +      for (unsigned ix = 0; ix != n_conditions; ix++)
>>>>>> +    {
>>>>>> +      const unsigned blockno = gcov_read_unsigned ();
>>>>>> +      const unsigned nterms = gcov_read_unsigned ();
>>>>>> +
>>>>>> + ��    printf ("\n");
>>>>>> +      print_prefix (filename, depth, gcov_position ());
>>>>>> +      printf (VALUE_PADDING_PREFIX "block %u:", blockno);
>>>>>> +      printf (" %u", nterms);
>>>>>> +    }
>>>>>> +    }
>>>>>> +}
>>>>>>   static void
>>>>>>   tag_lines (const char *filename ATTRIBUTE_UNUSED,
>>>>>>          unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
>>>>>> diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h
>>>>>> index e6f33e32652..eda4611ac42 100644
>>>>>> --- a/gcc/gcov-io.h
>>>>>> +++ b/gcc/gcov-io.h
>>>>>> @@ -261,6 +261,9 @@ typedef uint64_t gcov_type_unsigned;
>>>>>>   #define GCOV_TAG_ARCS         ((gcov_unsigned_t)0x01430000)
>>>>>>   #define GCOV_TAG_ARCS_LENGTH(NUM)  (1 + (NUM) * 2 * GCOV_WORD_SIZE)
>>>>>>   #define GCOV_TAG_ARCS_NUM(LENGTH)  (((LENGTH / GCOV_WORD_SIZE) - 
>>>>>> 1) / 2)
>>>>>> +#define GCOV_TAG_CONDS           ((gcov_unsigned_t)0x01470000)
>>>>>> +#define GCOV_TAG_CONDS_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE)
>>>>>> +#define GCOV_TAG_CONDS_NUM(LENGTH) (((LENGTH) / GCOV_WORD_SIZE) / 2)
>>>>>>   #define GCOV_TAG_LINES         ((gcov_unsigned_t)0x01450000)
>>>>>>   #define GCOV_TAG_COUNTER_BASE      ((gcov_unsigned_t)0x01a10000)
>>>>>>   #define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE)
>>>>>> diff --git a/gcc/gcov.cc b/gcc/gcov.cc
>>>>>> index 8b4748d3a1a..22aa58b6cd9 100644
>>>>>> --- a/gcc/gcov.cc
>>>>>> +++ b/gcc/gcov.cc
>>>>>> @@ -46,6 +46,7 @@ along with Gcov; see the file COPYING3.  If not see
>>>>>>   #include "color-macros.h"
>>>>>>   #include "pretty-print.h"
>>>>>>   #include "json.h"
>>>>>> +#include "hwint.h"
>>>>>>   #include <zlib.h>
>>>>>>   #include <getopt.h>
>>>>>> @@ -81,6 +82,7 @@ using namespace std;
>>>>>>   class function_info;
>>>>>>   class block_info;
>>>>>>   class source_info;
>>>>>> +class condition_info;
>>>>>>   /* Describes an arc between two basic blocks.  */
>>>>>> @@ -134,6 +136,33 @@ public:
>>>>>>     vector<unsigned> lines;
>>>>>>   };
>>>>>> +/* Describes a single conditional expression and the (recorded) 
>>>>>> conditions
>>>>>> +   shown to independently affect the outcome.  */
>>>>>> +class condition_info
>>>>>> +{
>>>>>> +public:
>>>>>> +  condition_info ();
>>>>>> +
>>>>>> +  int popcount () const;
>>>>>> +
>>>>>> +  /* Bitsets storing the independently significant outcomes for 
>>>>>> true and false,
>>>>>> +   * respectively.  */
>>>>>> +  gcov_type_unsigned truev;
>>>>>> +  gcov_type_unsigned falsev;
>>>>>> +
>>>>>> +  /* Number of terms in the expression; if (x) -> 1, if (x && y) 
>>>>>> -> 2 etc.  */
>>>>>> +  unsigned n_terms;
>>>>>> +};
>>>>>> +
>>>>>> +condition_info::condition_info (): truev (0), falsev (0), n_terms 
>>>>>> (0)
>>>>>> +{
>>>>>> +}
>>>>>> +
>>>>>> +int condition_info::popcount () const
>>>>>> +{
>>>>>> +    return popcount_hwi (truev) + popcount_hwi (falsev);
>>>>>> +}
>>>>>> +
>>>>>>   /* Describes a basic block. Contains lists of arcs to successor and
>>>>>>      predecessor blocks.  */
>>>>>> @@ -167,6 +196,8 @@ public:
>>>>>>     /* Block is a landing pad for longjmp or throw.  */
>>>>>>     unsigned is_nonlocal_return : 1;
>>>>>> +  condition_info conditions;
>>>>>> +
>>>>>>     vector<block_location_info> locations;
>>>>>>     struct
>>>>>> @@ -277,6 +308,8 @@ public:
>>>>>>     vector<block_info> blocks;
>>>>>>     unsigned blocks_executed;
>>>>>> +  vector<condition_info*> conditions;
>>>>>> +
>>>>>>     /* Raw arc coverage counts.  */
>>>>>>     vector<gcov_type> counts;
>>>>>> @@ -353,6 +386,9 @@ struct coverage_info
>>>>>>     int branches_executed;
>>>>>>     int branches_taken;
>>>>>> +  int conditions;
>>>>>> +  int conditions_covered;
>>>>>> +
>>>>>>     int calls;
>>>>>>     int calls_executed;
>>>>>> @@ -552,6 +588,10 @@ static int multiple_files = 0;
>>>>>>   static int flag_branches = 0;
>>>>>> +/* Output conditions (modified condition/decision coverage).  */
>>>>>> +
>>>>>> +static bool flag_conditions = 0;
>>>>>> +
>>>>>>   /* Show unconditional branches too.  */
>>>>>>   static int flag_unconditional = 0;
>>>>>> @@ -658,6 +698,7 @@ static int read_count_file (void);
>>>>>>   static void solve_flow_graph (function_info *);
>>>>>>   static void find_exception_blocks (function_info *);
>>>>>>   static void add_branch_counts (coverage_info *, const arc_info *);
>>>>>> +static void add_condition_counts (coverage_info *, const 
>>>>>> block_info *);
>>>>>>   static void add_line_counts (coverage_info *, function_info *);
>>>>>>   static void executed_summary (unsigned, unsigned);
>>>>>>   static void function_summary (const coverage_info *);
>>>>>> @@ -666,6 +707,7 @@ static const char *format_gcov (gcov_type, 
>>>>>> gcov_type, int);
>>>>>>   static void accumulate_line_counts (source_info *);
>>>>>>   static void output_gcov_file (const char *, source_info *);
>>>>>>   static int output_branch_count (FILE *, int, const arc_info *);
>>>>>> +static void output_conditions (FILE *, const block_info *);
>>>>>>   static void output_lines (FILE *, const source_info *);
>>>>>>   static string make_gcov_file_name (const char *, const char *);
>>>>>>   static char *mangle_name (const char *);
>>>>>> @@ -930,6 +972,8 @@ print_usage (int error_p)
>>>>>>     fnotice (file, "  -b, --branch-probabilities      Include 
>>>>>> branch probabilities in output\n");
>>>>>>     fnotice (file, "  -c, --branch-counts             Output 
>>>>>> counts of branches taken\n\
>>>>>>                                       rather than percentages\n");
>>>>>> +  fnotice (file, "  -g, --conditions                Include 
>>>>>> modified condition/decision\n\
>>>>>> +                                    coverage in output\n");
>>>>>>     fnotice (file, "  -d, --display-progress          Display 
>>>>>> progress information\n");
>>>>>>     fnotice (file, "  -D, --debug                Display debugging 
>>>>>> dumps\n");
>>>>>>     fnotice (file, "  -f, --function-summaries        Output 
>>>>>> summaries for each function\n");
>>>>>> @@ -983,6 +1027,7 @@ static const struct option options[] =
>>>>>>     { "all-blocks",           no_argument,       NULL, 'a' },
>>>>>>     { "branch-probabilities", no_argument,       NULL, 'b' },
>>>>>>     { "branch-counts",        no_argument,       NULL, 'c' },
>>>>>> +  { "conditions",        no_argument,       NULL, 'g' },
>>>>>>     { "json-format",        no_argument,       NULL, 'j' },
>>>>>>     { "human-readable",        no_argument,       NULL, 'H' },
>>>>>>     { "no-output",            no_argument,       NULL, 'n' },
>>>>>> @@ -1011,7 +1056,7 @@ process_args (int argc, char **argv)
>>>>>>   {
>>>>>>     int opt;
>>>>>> -  const char *opts = "abcdDfhHijklmno:pqrs:tuvwx";
>>>>>> +  const char *opts = "abcdDfghHijklmno:pqrs:tuvwx";
>>>>>>     while ((opt = getopt_long (argc, argv, opts, options, NULL)) 
>>>>>> != -1)
>>>>>>       {
>>>>>>         switch (opt)
>>>>>> @@ -1028,6 +1073,9 @@ process_args (int argc, char **argv)
>>>>>>       case 'f':
>>>>>>         flag_function_summary = 1;
>>>>>>         break;
>>>>>> +    case 'g':
>>>>>> +      flag_conditions = 1;
>>>>>> +      break;
>>>>>>       case 'h':
>>>>>>         print_usage (false);
>>>>>>         /* print_usage will exit.  */
>>>>>> @@ -1152,6 +1200,45 @@ output_intermediate_json_line (json::array 
>>>>>> *object,
>>>>>>         }
>>>>>>         }
>>>>>> +  json::array *conditions = new json::array ();
>>>>>> +  lineo->set ("conditions", conditions);
>>>>>> +  if (flag_conditions)
>>>>>> +  {
>>>>>> +    vector<block_info *>::const_iterator it;
>>>>>> +    for (it = line->blocks.begin (); it != line->blocks.end (); 
>>>>>> it++)
>>>>>> +      {
>>>>>> +    const condition_info& info = (*it)->conditions;
>>>>>> +    if (info.n_terms == 0)
>>>>>> +        continue;
>>>>>> +
>>>>>> +    const int count = 2 * info.n_terms;
>>>>>> +    const int covered = info.popcount ();
>>>>>> +
>>>>>> +    json::object *cond = new json::object ();
>>>>>> +    cond->set ("count", new json::integer_number (count));
>>>>>> +    cond->set ("covered", new json::integer_number (covered));
>>>>>> +
>>>>>> +    json::array *mtrue = new json::array ();
>>>>>> +    json::array *mfalse = new json::array ();
>>>>>> +    cond->set ("not_covered_true", mtrue);
>>>>>> +    cond->set ("not_covered_false", mfalse);
>>>>>> +
>>>>>> +    if (count != covered)
>>>>>> +      {
>>>>>> +        for (unsigned i = 0; i < info.n_terms; i++)
>>>>>> +          {
>>>>>> +        gcov_type_unsigned index = 1;
>>>>>> +        index <<= i;
>>>>>> +        if (!(index & info.truev))
>>>>>> +            mtrue->append (new json::integer_number (i));
>>>>>> +        if (!(index & info.falsev))
>>>>>> +            mfalse->append (new json::integer_number (i));
>>>>>> +          }
>>>>>> +      }
>>>>>> +    conditions->append (cond);
>>>>>> +      }
>>>>>> +  }
>>>>>> +
>>>>>>     object->append (lineo);
>>>>>>   }
>>>>>> @@ -1969,6 +2056,28 @@ read_graph_file (void)
>>>>>>             }
>>>>>>           }
>>>>>>       }
>>>>>> +      else if (fn && tag == GCOV_TAG_CONDS)
>>>>>> +    {
>>>>>> +      unsigned num_dests = GCOV_TAG_CONDS_NUM (length);
>>>>>> +
>>>>>> +      if (!fn->conditions.empty ())
>>>>>> +        fnotice (stderr, "%s:already seen conditions for '%s'\n",
>>>>>> +             bbg_file_name, fn->get_name ());
>>>>>> +      else
>>>>>> +        fn->conditions.resize (num_dests);
>>>>>> +
>>>>>> +      for (unsigned i = 0; i < num_dests; ++i)
>>>>>> +        {
>>>>>> +          unsigned idx = gcov_read_unsigned ();
>>>>>> +
>>>>>> +          if (idx >= fn->blocks.size ())
>>>>>> +        goto corrupt;
>>>>>> +
>>>>>> +          condition_info *info = &fn->blocks[idx].conditions;
>>>>>> +          info->n_terms = gcov_read_unsigned ();
>>>>>> +          fn->conditions[i] = info;
>>>>>> +        }
>>>>>> +    }
>>>>>>         else if (fn && tag == GCOV_TAG_LINES)
>>>>>>       {
>>>>>>         unsigned blockno = gcov_read_unsigned ();
>>>>>> @@ -2099,6 +2208,21 @@ read_count_file (void)
>>>>>>             goto cleanup;
>>>>>>           }
>>>>>>       }
>>>>>> +      else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_CONDS) 
>>>>>> && fn)
>>>>>> +    {
>>>>>> +      length = abs (read_length);
>>>>>> +      if (length != GCOV_TAG_COUNTER_LENGTH (2 * 
>>>>>> fn->conditions.size ()))
>>>>>> +          goto mismatch;
>>>>>> +
>>>>>> +      if (read_length > 0)
>>>>>> +        {
>>>>>> +          for (ix = 0; ix != fn->conditions.size (); ix++)
>>>>>> +        {
>>>>>> +          fn->conditions[ix]->truev  |= gcov_read_counter ();
>>>>>> +          fn->conditions[ix]->falsev |= gcov_read_counter ();
>>>>>> +        }
>>>>>> +        }
>>>>>> +    }
>>>>>>         else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) 
>>>>>> && fn)
>>>>>>       {
>>>>>>         length = abs (read_length);
>>>>>> @@ -2443,6 +2567,15 @@ add_branch_counts (coverage_info *coverage, 
>>>>>> const arc_info *arc)
>>>>>>       }
>>>>>>   }
>>>>>> +/* Increment totals in COVERAGE according to to block BLOCK.  */
>>>>>> +
>>>>>> +static void
>>>>>> +add_condition_counts (coverage_info *coverage, const block_info 
>>>>>> *block)
>>>>>> +{
>>>>>> +  coverage->conditions += 2 * block->conditions.n_terms;
>>>>>> +  coverage->conditions_covered += block->conditions.popcount ();
>>>>>> +}
>>>>>> +
>>>>>>   /* Format COUNT, if flag_human_readable_numbers is set, return 
>>>>>> it human
>>>>>>      readable format.  */
>>>>>> @@ -2546,6 +2679,18 @@ file_summary (const coverage_info *coverage)
>>>>>>            coverage->calls);
>>>>>>         else
>>>>>>       fnotice (stdout, "No calls\n");
>>>>>> +
>>>>>> +    }
>>>>>> +
>>>>>> +  if (flag_conditions)
>>>>>> +    {
>>>>>> +      if (coverage->conditions)
>>>>>> +    fnotice (stdout, "Condition outcomes covered:%s of %d\n",
>>>>>> +         format_gcov (coverage->conditions_covered,
>>>>>> +                  coverage->conditions, 2),
>>>>>> +         coverage->conditions);
>>>>>> +      else
>>>>>> +    fnotice (stdout, "No conditions\n");
>>>>>>       }
>>>>>>   }
>>>>>> @@ -2780,6 +2925,12 @@ static void accumulate_line_info (line_info 
>>>>>> *line, source_info *src,
>>>>>>        it != line->branches.end (); it++)
>>>>>>         add_branch_counts (&src->coverage, *it);
>>>>>> +  if (add_coverage)
>>>>>> +    for (vector<block_info *>::iterator it = line->blocks.begin ();
>>>>>> +     it != line->blocks.end (); it++)
>>>>>> +      add_condition_counts (&src->coverage, *it);
>>>>>> +
>>>>>> +
>>>>>>     if (!line->blocks.empty ())
>>>>>>       {
>>>>>>         /* The user expects the line count to be the number of times
>>>>>> @@ -2881,6 +3032,37 @@ accumulate_line_counts (source_info *src)
>>>>>>         }
>>>>>>   }
>>>>>> +/* Output information about the conditions in block BINFO.  The 
>>>>>> output includes
>>>>>> + * a summary (n/m outcomes covered) and a list of the missing 
>>>>>> (uncovered)
>>>>>> + * outcomes.  */
>>>>>> +
>>>>>> +static void
>>>>>> +output_conditions (FILE *gcov_file, const block_info *binfo)
>>>>>> +{
>>>>>> +    const condition_info& info = binfo->conditions;
>>>>>> +    if (info.n_terms == 0)
>>>>>> +    return;
>>>>>> +
>>>>>> +    const int expected = 2 * info.n_terms;
>>>>>> +    const int got = info.popcount ();
>>>>>> +
>>>>>> +    fnotice (gcov_file, "condition outcomes covered %d/%d\n", 
>>>>>> got, expected);
>>>>>> +    if (expected == got)
>>>>>> +    return;
>>>>>> +
>>>>>> +    for (unsigned i = 0; i < info.n_terms; i++)
>>>>>> +    {
>>>>>> +    gcov_type_unsigned index = 1;
>>>>>> +    index <<= i;
>>>>>> +    if ((index & info.truev & info.falsev))
>>>>>> +        continue;
>>>>>> +
>>>>>> +    const char *t = (index & info.truev) ? "" : "true";
>>>>>> +    const char *f = (index & info.falsev) ? "" : " false";
>>>>>> +    fnotice (gcov_file, "condition %2u not covered (%s%s)\n", i, 
>>>>>> t, f + !t[0]);
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>>   /* Output information about ARC number IX.  Returns nonzero if
>>>>>>      anything is output.  */
>>>>>> @@ -3091,16 +3273,29 @@ output_line_details (FILE *f, const 
>>>>>> line_info *line, unsigned line_num)
>>>>>>         if (flag_branches)
>>>>>>           for (arc = (*it)->succ; arc; arc = arc->succ_next)
>>>>>>             jx += output_branch_count (f, jx, arc);
>>>>>> +
>>>>>> +      if (flag_conditions)
>>>>>> +          output_conditions (f, *it);
>>>>>>       }
>>>>>>       }
>>>>>> -  else if (flag_branches)
>>>>>> +  else
>>>>>>       {
>>>>>> -      int ix;
>>>>>> +      if (flag_branches)
>>>>>> +    {
>>>>>> +      int ix;
>>>>>> +
>>>>>> +      ix = 0;
>>>>>> +      for (vector<arc_info *>::const_iterator it = 
>>>>>> line->branches.begin ();
>>>>>> +          it != line->branches.end (); it++)
>>>>>> +          ix += output_branch_count (f, ix, (*it));
>>>>>> +    }
>>>>>> -      ix = 0;
>>>>>> -      for (vector<arc_info *>::const_iterator it = 
>>>>>> line->branches.begin ();
>>>>>> -       it != line->branches.end (); it++)
>>>>>> -    ix += output_branch_count (f, ix, (*it));
>>>>>> +      if (flag_conditions)
>>>>>> +    {
>>>>>> +      for (vector<block_info *>::const_iterator it = 
>>>>>> line->blocks.begin ();
>>>>>> +           it != line->blocks.end (); it++)
>>>>>> +          output_conditions (f, *it);
>>>>>> +    }
>>>>>>       }
>>>>>>   }
>>>>>> diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
>>>>>> index 342e43a7f25..591cb50193c 100644
>>>>>> --- a/gcc/gimplify.cc
>>>>>> +++ b/gcc/gimplify.cc
>>>>>> @@ -71,6 +71,24 @@ along with GCC; see the file COPYING3.  If not see
>>>>>>   #include "context.h"
>>>>>>   #include "tree-nested.h"
>>>>>> +static unsigned nextuid = 1;
>>>>>> +/* Get a fresh identifier for a new condition expression.  This 
>>>>>> is used for
>>>>>> +   condition coverage.  */
>>>>>> +static unsigned
>>>>>> +next_cond_uid ()
>>>>>> +{
>>>>>> +    return nextuid++;
>>>>>> +}
>>>>>> +/* Reset the condition uid to the value it should have when 
>>>>>> compiling a new
>>>>>> +   function.  0 is already the default/untouched value, so start 
>>>>>> at non-zero.
>>>>>> +   A valid and set id should always be > 0.  This is used for 
>>>>>> condition
>>>>>> +   coverage.  */
>>>>>> +static void
>>>>>> +reset_cond_uid ()
>>>>>> +{
>>>>>> +    nextuid = 1;
>>>>>> +}
>>>>>> +
>>>>>>   /* Hash set of poisoned variables in a bind expr.  */
>>>>>>   static hash_set<tree> *asan_poisoned_variables = NULL;
>>>>>> @@ -4131,13 +4149,16 @@ gimplify_call_expr (tree *expr_p, 
>>>>>> gimple_seq *pre_p, bool want_value)
>>>>>>      LOCUS is the source location of the COND_EXPR.
>>>>>> +   The condition_uid is a discriminator tag for condition 
>>>>>> coverage used to map
>>>>>> +   conditions to its corresponding full Boolean function.
>>>>>> +
>>>>>>      This function is the tree equivalent of do_jump.
>>>>>>      shortcut_cond_r should only be called by shortcut_cond_expr.  */
>>>>>>   static tree
>>>>>>   shortcut_cond_r (tree pred, tree *true_label_p, tree 
>>>>>> *false_label_p,
>>>>>> -         location_t locus)
>>>>>> +         location_t locus, unsigned condition_uid)
>>>>>>   {
>>>>>>     tree local_label = NULL_TREE;
>>>>>>     tree t, expr = NULL;
>>>>>> @@ -4159,13 +4180,14 @@ shortcut_cond_r (tree pred, tree 
>>>>>> *true_label_p, tree *false_label_p,
>>>>>>       false_label_p = &local_label;
>>>>>>         /* Keep the original source location on the first 'if'.  */
>>>>>> -      t = shortcut_cond_r (TREE_OPERAND (pred, 0), NULL, 
>>>>>> false_label_p, locus);
>>>>>> +      t = shortcut_cond_r (TREE_OPERAND (pred, 0), NULL, 
>>>>>> false_label_p, locus,
>>>>>> +               condition_uid);
>>>>>>         append_to_statement_list (t, &expr);
>>>>>>         /* Set the source location of the && on the second 'if'.  */
>>>>>>         new_locus = rexpr_location (pred, locus);
>>>>>>         t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p, 
>>>>>> false_label_p,
>>>>>> -               new_locus);
>>>>>> +               new_locus, condition_uid);
>>>>>>         append_to_statement_list (t, &expr);
>>>>>>       }
>>>>>>     else if (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
>>>>>> @@ -4182,13 +4204,14 @@ shortcut_cond_r (tree pred, tree 
>>>>>> *true_label_p, tree *false_label_p,
>>>>>>       true_label_p = &local_label;
>>>>>>         /* Keep the original source location on the first 'if'.  */
>>>>>> -      t = shortcut_cond_r (TREE_OPERAND (pred, 0), true_label_p, 
>>>>>> NULL, locus);
>>>>>> +      t = shortcut_cond_r (TREE_OPERAND (pred, 0), true_label_p, 
>>>>>> NULL, locus,
>>>>>> +               condition_uid);
>>>>>>         append_to_statement_list (t, &expr);
>>>>>>         /* Set the source location of the || on the second 'if'.  */
>>>>>>         new_locus = rexpr_location (pred, locus);
>>>>>>         t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p, 
>>>>>> false_label_p,
>>>>>> -               new_locus);
>>>>>> +               new_locus, condition_uid);
>>>>>>         append_to_statement_list (t, &expr);
>>>>>>       }
>>>>>>     else if (TREE_CODE (pred) == COND_EXPR
>>>>>> @@ -4211,9 +4234,11 @@ shortcut_cond_r (tree pred, tree 
>>>>>> *true_label_p, tree *false_label_p,
>>>>>>         new_locus = rexpr_location (pred, locus);
>>>>>>         expr = build3 (COND_EXPR, void_type_node, TREE_OPERAND 
>>>>>> (pred, 0),
>>>>>>                shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
>>>>>> -                      false_label_p, locus),
>>>>>> +                      false_label_p, locus, condition_uid),
>>>>>>                shortcut_cond_r (TREE_OPERAND (pred, 2), true_label_p,
>>>>>> -                      false_label_p, new_locus));
>>>>>> +                      false_label_p, new_locus,
>>>>>> +                      condition_uid));
>>>>>> +      SET_EXPR_UID (expr, condition_uid);
>>>>>>       }
>>>>>>     else
>>>>>>       {
>>>>>> @@ -4221,6 +4246,7 @@ shortcut_cond_r (tree pred, tree 
>>>>>> *true_label_p, tree *false_label_p,
>>>>>>                build_and_jump (true_label_p),
>>>>>>                build_and_jump (false_label_p));
>>>>>>         SET_EXPR_LOCATION (expr, locus);
>>>>>> +      SET_EXPR_UID (expr, condition_uid);
>>>>>>       }
>>>>>>     if (local_label)
>>>>>> @@ -4271,12 +4297,41 @@ find_goto_label (tree expr)
>>>>>>     return NULL_TREE;
>>>>>>   }
>>>>>> +
>>>>>> +/* Given a multi-term condition (ANDIF, ORIF), walk the predicate 
>>>>>> and tag every
>>>>>> +   term with uid.  When two basic conditions share the uid 
>>>>>> discriminator when
>>>>>> +   they belong to the same predicate, which used by the condition 
>>>>>> coverage.
>>>>>> +   Doing this as an explicit step makes for a simpler 
>>>>>> implementation than
>>>>>> +   weaving it into the splitting code as the splitting code 
>>>>>> eventually reaches
>>>>>> +   back up to gimplfiy_expr which makes bookkeeping complicated.  */
>>>>>> +static void
>>>>>> +tag_shortcut_cond (tree pred, unsigned condition_uid)
>>>>>> +{
>>>>>> +  if (TREE_CODE (pred) == TRUTH_ANDIF_EXPR
>>>>>> +      || TREE_CODE (pred) == TRUTH_ORIF_EXPR)
>>>>>> +    {
>>>>>> +      tree fst = TREE_OPERAND (pred, 0);
>>>>>> +      tree lst = TREE_OPERAND (pred, 1);
>>>>>> +
>>>>>> +      if (TREE_CODE (fst) == TRUTH_ANDIF_EXPR
>>>>>> +      || TREE_CODE (fst) == TRUTH_ORIF_EXPR)
>>>>>> +    tag_shortcut_cond (fst, condition_uid);
>>>>>> +      else if (TREE_CODE (fst) == COND_EXPR)
>>>>>> +    SET_EXPR_UID (fst, condition_uid);
>>>>>> +
>>>>>> +      if (TREE_CODE (lst) == TRUTH_ANDIF_EXPR
>>>>>> +      || TREE_CODE (lst) == TRUTH_ORIF_EXPR)
>>>>>> +    tag_shortcut_cond (lst, condition_uid);
>>>>>> +      else if (TREE_CODE (lst) == COND_EXPR)
>>>>>> +    SET_EXPR_UID (lst, condition_uid);
>>>>>> +    }
>>>>>> +}
>>>>>>   /* Given a conditional expression EXPR with short-circuit boolean
>>>>>>      predicates using TRUTH_ANDIF_EXPR or TRUTH_ORIF_EXPR, break the
>>>>>>      predicate apart into the equivalent sequence of 
>>>>>> conditionals.  */
>>>>>>   static tree
>>>>>> -shortcut_cond_expr (tree expr)
>>>>>> +shortcut_cond_expr (tree expr, unsigned condition_uid)
>>>>>>   {
>>>>>>     tree pred = TREE_OPERAND (expr, 0);
>>>>>>     tree then_ = TREE_OPERAND (expr, 1);
>>>>>> @@ -4288,6 +4343,8 @@ shortcut_cond_expr (tree expr)
>>>>>>     bool then_se = then_ && TREE_SIDE_EFFECTS (then_);
>>>>>>     bool else_se = else_ && TREE_SIDE_EFFECTS (else_);
>>>>>> +  tag_shortcut_cond (pred, condition_uid);
>>>>>> +
>>>>>>     /* First do simple transformations.  */
>>>>>>     if (!else_se)
>>>>>>       {
>>>>>> @@ -4303,7 +4360,7 @@ shortcut_cond_expr (tree expr)
>>>>>>         /* Set the source location of the && on the second 'if'.  */
>>>>>>         if (rexpr_has_location (pred))
>>>>>>           SET_EXPR_LOCATION (expr, rexpr_location (pred));
>>>>>> -      then_ = shortcut_cond_expr (expr);
>>>>>> +      then_ = shortcut_cond_expr (expr, condition_uid);
>>>>>>         then_se = then_ && TREE_SIDE_EFFECTS (then_);
>>>>>>         pred = TREE_OPERAND (pred, 0);
>>>>>>         expr = build3 (COND_EXPR, void_type_node, pred, then_, 
>>>>>> NULL_TREE);
>>>>>> @@ -4325,7 +4382,7 @@ shortcut_cond_expr (tree expr)
>>>>>>         /* Set the source location of the || on the second 'if'.  */
>>>>>>         if (rexpr_has_location (pred))
>>>>>>           SET_EXPR_LOCATION (expr, rexpr_location (pred));
>>>>>> -      else_ = shortcut_cond_expr (expr);
>>>>>> +      else_ = shortcut_cond_expr (expr, condition_uid);
>>>>>>         else_se = else_ && TREE_SIDE_EFFECTS (else_);
>>>>>>         pred = TREE_OPERAND (pred, 0);
>>>>>>         expr = build3 (COND_EXPR, void_type_node, pred, NULL_TREE, 
>>>>>> else_);
>>>>>> @@ -4333,6 +4390,9 @@ shortcut_cond_expr (tree expr)
>>>>>>       }
>>>>>>       }
>>>>>> +  /* The expr tree should also have the expression id set.  */
>>>>>> +  SET_EXPR_UID (expr, condition_uid);
>>>>>> +
>>>>>>     /* If we're done, great.  */
>>>>>>     if (TREE_CODE (pred) != TRUTH_ANDIF_EXPR
>>>>>>         && TREE_CODE (pred) != TRUTH_ORIF_EXPR)
>>>>>> @@ -4380,7 +4440,7 @@ shortcut_cond_expr (tree expr)
>>>>>>     /* If there was nothing else in our arms, just forward the 
>>>>>> label(s).  */
>>>>>>     if (!then_se && !else_se)
>>>>>>       return shortcut_cond_r (pred, true_label_p, false_label_p,
>>>>>> -                EXPR_LOC_OR_LOC (expr, input_location));
>>>>>> +                EXPR_LOC_OR_LOC (expr, input_location), 
>>>>>> condition_uid);
>>>>>>     /* If our last subexpression already has a terminal label, 
>>>>>> reuse it.  */
>>>>>>     if (else_se)
>>>>>> @@ -4412,7 +4472,8 @@ shortcut_cond_expr (tree expr)
>>>>>>     jump_over_else = block_may_fallthru (then_);
>>>>>>     pred = shortcut_cond_r (pred, true_label_p, false_label_p,
>>>>>> -              EXPR_LOC_OR_LOC (expr, input_location));
>>>>>> +              EXPR_LOC_OR_LOC (expr, input_location),
>>>>>> +              condition_uid);
>>>>>>     expr = NULL;
>>>>>>     append_to_statement_list (pred, &expr);
>>>>>> @@ -4688,7 +4749,7 @@ gimplify_cond_expr (tree *expr_p, gimple_seq 
>>>>>> *pre_p, fallback_t fallback)
>>>>>>     if (TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ANDIF_EXPR
>>>>>>         || TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ORIF_EXPR)
>>>>>>       {
>>>>>> -      expr = shortcut_cond_expr (expr);
>>>>>> +      expr = shortcut_cond_expr (expr, next_cond_uid ());
>>>>>>         if (expr != *expr_p)
>>>>>>       {
>>>>>> @@ -4752,11 +4813,16 @@ gimplify_cond_expr (tree *expr_p, 
>>>>>> gimple_seq *pre_p, fallback_t fallback)
>>>>>>     else
>>>>>>       label_false = create_artificial_label (UNKNOWN_LOCATION);
>>>>>> +  unsigned cond_uid = EXPR_COND_UID (expr);
>>>>>> +  if (cond_uid == 0)
>>>>>> +    cond_uid = next_cond_uid ();
>>>>>> +
>>>>>>     gimple_cond_get_ops_from_tree (COND_EXPR_COND (expr), 
>>>>>> &pred_code, &arm1,
>>>>>>                    &arm2);
>>>>>>     cond_stmt = gimple_build_cond (pred_code, arm1, arm2, label_true,
>>>>>>                    label_false);
>>>>>>     gimple_set_location (cond_stmt, EXPR_LOCATION (expr));
>>>>>> +  cfun->cond_uids->put (cond_stmt, cond_uid);
>>>>>>     copy_warning (cond_stmt, COND_EXPR_COND (expr));
>>>>>>     gimplify_seq_add_stmt (&seq, cond_stmt);
>>>>>>     gimple_stmt_iterator gsi = gsi_last (seq);
>>>>>> @@ -18176,6 +18242,8 @@ gimplify_function_tree (tree fndecl)
>>>>>>     else
>>>>>>       push_struct_function (fndecl);
>>>>>> +  reset_cond_uid ();
>>>>>> +
>>>>>>     /* Tentatively set PROP_gimple_lva here, and reset it in 
>>>>>> gimplify_va_arg_expr
>>>>>>        if necessary.  */
>>>>>>     cfun->curr_properties |= PROP_gimple_lva;
>>>>>> diff --git a/gcc/ipa-inline.cc b/gcc/ipa-inline.cc
>>>>>> index dc120e6da5a..9502a21c741 100644
>>>>>> --- a/gcc/ipa-inline.cc
>>>>>> +++ b/gcc/ipa-inline.cc
>>>>>> @@ -682,7 +682,7 @@ can_early_inline_edge_p (struct cgraph_edge *e)
>>>>>>       }
>>>>>>     gcc_assert (gimple_in_ssa_p (DECL_STRUCT_FUNCTION 
>>>>>> (e->caller->decl))
>>>>>>             && gimple_in_ssa_p (DECL_STRUCT_FUNCTION 
>>>>>> (callee->decl)));
>>>>>> -  if (profile_arc_flag
>>>>>> +  if ((profile_arc_flag || condition_coverage_flag)
>>>>>>         && ((lookup_attribute ("no_profile_instrument_function",
>>>>>>                   DECL_ATTRIBUTES (caller->decl)) == NULL_TREE)
>>>>>>         != (lookup_attribute ("no_profile_instrument_function",
>>>>>> diff --git a/gcc/ipa-split.cc b/gcc/ipa-split.cc
>>>>>> index 6730f4f9d0e..ca3bd5e3529 100644
>>>>>> --- a/gcc/ipa-split.cc
>>>>>> +++ b/gcc/ipa-split.cc
>>>>>> @@ -1930,7 +1930,7 @@ pass_split_functions::gate (function *)
>>>>>>     /* When doing profile feedback, we want to execute the pass 
>>>>>> after profiling
>>>>>>        is read.  So disable one in early optimization.  */
>>>>>>     return (flag_partial_inlining
>>>>>> -      && !profile_arc_flag && !flag_branch_probabilities);
>>>>>> +      && !profile_arc_flag && !flag_branch_probabilities);
>>>>>>   }
>>>>>>   } // anon namespace
>>>>>> diff --git a/gcc/passes.cc b/gcc/passes.cc
>>>>>> index 087aed52934..110a6b0b174 100644
>>>>>> --- a/gcc/passes.cc
>>>>>> +++ b/gcc/passes.cc
>>>>>> @@ -352,7 +352,8 @@ finish_optimization_passes (void)
>>>>>>     gcc::dump_manager *dumps = m_ctxt->get_dumps ();
>>>>>>     timevar_push (TV_DUMP);
>>>>>> -  if (profile_arc_flag || flag_test_coverage || 
>>>>>> flag_branch_probabilities)
>>>>>> +  if (profile_arc_flag || condition_coverage_flag || 
>>>>>> flag_test_coverage
>>>>>> +      || flag_branch_probabilities)
>>>>>>       {
>>>>>>         dumps->dump_start (pass_profile_1->static_pass_number, NULL);
>>>>>>         end_branch_prob ();
>>>>>> diff --git a/gcc/profile.cc b/gcc/profile.cc
>>>>>> index fc59326a19b..bff3b96d871 100644
>>>>>> --- a/gcc/profile.cc
>>>>>> +++ b/gcc/profile.cc
>>>>>> @@ -69,6 +69,17 @@ along with GCC; see the file COPYING3.  If not see
>>>>>>   #include "profile.h"
>>>>>> +struct condcov;
>>>>>> +struct condcov *find_conditions (struct function*);
>>>>>> +size_t cov_length (const struct condcov*);
>>>>>> +array_slice<basic_block> cov_blocks (struct condcov*, size_t);
>>>>>> +array_slice<uint64_t> cov_masks (struct condcov*, size_t);
>>>>>> +array_slice<sbitmap> cov_maps (struct condcov* cov, size_t n);
>>>>>> +void cov_free (struct condcov*);
>>>>>> +size_t instrument_decisions (array_slice<basic_block>, size_t,
>>>>>> +                 array_slice<sbitmap>,
>>>>>> +                 array_slice<gcov_type_unsigned>);
>>>>>> +
>>>>>>   /* Map from BBs/edges to gcov counters.  */
>>>>>>   vec<gcov_type> bb_gcov_counts;
>>>>>>   hash_map<edge,gcov_type> *edge_gcov_counts;
>>>>>> @@ -100,6 +111,7 @@ static int total_num_passes;
>>>>>>   static int total_num_times_called;
>>>>>>   static int total_hist_br_prob[20];
>>>>>>   static int total_num_branches;
>>>>>> +static int total_num_conds;
>>>>>>   /* Forward declarations.  */
>>>>>>   static void find_spanning_tree (struct edge_list *);
>>>>>> @@ -1155,6 +1167,12 @@ read_thunk_profile (struct cgraph_node *node)
>>>>>>      the flow graph that are needed to reconstruct the dynamic 
>>>>>> behavior of the
>>>>>>      flow graph.  This data is written to the gcno file for gcov.
>>>>>> +   When FLAG_PROFILE_CONDITIONS is nonzero, this functions 
>>>>>> instruments the
>>>>>> +   edges in the control flow graph to track what conditions are 
>>>>>> evaluated to in
>>>>>> +   order to determine what conditions are covered and have an 
>>>>>> independent
>>>>>> +   effect on the outcome (modified condition/decision coverage). 
>>>>>> This data is
>>>>>> +   written to the gcno file for gcov.
>>>>>> +
>>>>>>      When FLAG_BRANCH_PROBABILITIES is nonzero, this function 
>>>>>> reads auxiliary
>>>>>>      information from the gcda file containing edge count 
>>>>>> information from
>>>>>>      previous executions of the function being compiled.  In this 
>>>>>> case, the
>>>>>> @@ -1173,6 +1191,7 @@ branch_prob (bool thunk)
>>>>>>     struct edge_list *el;
>>>>>>     histogram_values values = histogram_values ();
>>>>>>     unsigned cfg_checksum, lineno_checksum;
>>>>>> +  bool output_to_file;
>>>>>>     total_num_times_called++;
>>>>>> @@ -1397,10 +1416,18 @@ branch_prob (bool thunk)
>>>>>>     /* Write the data from which gcov can reconstruct the basic block
>>>>>>        graph and function line numbers (the gcno file).  */
>>>>>> +  output_to_file = false;
>>>>>>     if (coverage_begin_function (lineno_checksum, cfg_checksum))
>>>>>>       {
>>>>>>         gcov_position_t offset;
>>>>>> +      /* The condition coverage needs a deeper analysis to 
>>>>>> identify expressions
>>>>>> +     of conditions, which means it is not yet ready to write to 
>>>>>> the gcno
>>>>>> +     file.  It will write its entries later, but needs to know if 
>>>>>> it do it
>>>>>> +     in the first place, which is controlled by the return value of
>>>>>> +     coverage_begin_function.  */
>>>>>> +      output_to_file = true;
>>>>>> +
>>>>>>         /* Basic block flags */
>>>>>>         offset = gcov_write_tag (GCOV_TAG_BLOCKS);
>>>>>>         gcov_write_unsigned (n_basic_blocks_for_fn (cfun));
>>>>>> @@ -1514,29 +1541,65 @@ branch_prob (bool thunk)
>>>>>>     remove_fake_edges ();
>>>>>> +  if (condition_coverage_flag || profile_arc_flag)
>>>>>> +      gimple_init_gcov_profiler ();
>>>>>> +
>>>>>> +  if (condition_coverage_flag)
>>>>>> +    {
>>>>>> +      struct condcov *cov = find_conditions (cfun);
>>>>>> +      gcc_assert (cov);
>>>>>> +      const size_t nconds = cov_length (cov);
>>>>>> +      total_num_conds += nconds;
>>>>>> +
>>>>>> +      if (coverage_counter_alloc (GCOV_COUNTER_CONDS, 2 * nconds))
>>>>>> +    {
>>>>>> +      gcov_position_t offset {};
>>>>>> +      if (output_to_file)
>>>>>> +          offset = gcov_write_tag (GCOV_TAG_CONDS);
>>>>>> +
>>>>>> +      for (size_t i = 0; i != nconds; ++i)
>>>>>> +        {
>>>>>> +          array_slice<basic_block> expr = cov_blocks (cov, i);
>>>>>> +          array_slice<uint64_t> masks = cov_masks (cov, i);
>>>>>> +          array_slice<sbitmap> maps = cov_maps (cov, i);
>>>>>> +          gcc_assert (expr.is_valid ());
>>>>>> +          gcc_assert (masks.is_valid ());
>>>>>> +          gcc_assert (maps.is_valid ());
>>>>>> +
>>>>>> +          size_t terms = instrument_decisions (expr, i, maps, 
>>>>>> masks);
>>>>>> +          if (output_to_file)
>>>>>> +        {
>>>>>> +          gcov_write_unsigned (expr.front ()->index);
>>>>>> +          gcov_write_unsigned (terms);
>>>>>> +        }
>>>>>> +        }
>>>>>> +      if (output_to_file)
>>>>>> +          gcov_write_length (offset);
>>>>>> +    }
>>>>>> +      cov_free (cov);
>>>>>> +    }
>>>>>> +
>>>>>>     /* For each edge not on the spanning tree, add counting code.  */
>>>>>>     if (profile_arc_flag
>>>>>>         && coverage_counter_alloc (GCOV_COUNTER_ARCS, 
>>>>>> num_instrumented))
>>>>>>       {
>>>>>>         unsigned n_instrumented;
>>>>>> -      gimple_init_gcov_profiler ();
>>>>>> -
>>>>>>         n_instrumented = instrument_edges (el);
>>>>>>         gcc_assert (n_instrumented == num_instrumented);
>>>>>>         if (flag_profile_values)
>>>>>>       instrument_values (values);
>>>>>> -
>>>>>> -      /* Commit changes done by instrumentation.  */
>>>>>> -      gsi_commit_edge_inserts ();
>>>>>>       }
>>>>>>     free_aux_for_edges ();
>>>>>>     values.release ();
>>>>>>     free_edge_list (el);
>>>>>> +  /* Commit changes done by instrumentation.  */
>>>>>> +  gsi_commit_edge_inserts ();
>>>>>> +
>>>>>>     coverage_end_function (lineno_checksum, cfg_checksum);
>>>>>>     if (flag_branch_probabilities
>>>>>>         && (profile_status_for_fn (cfun) == PROFILE_READ))
>>>>>> @@ -1669,6 +1732,7 @@ init_branch_prob (void)
>>>>>>     total_num_passes = 0;
>>>>>>     total_num_times_called = 0;
>>>>>>     total_num_branches = 0;
>>>>>> +  total_num_conds = 0;
>>>>>>     for (i = 0; i < 20; i++)
>>>>>>       total_hist_br_prob[i] = 0;
>>>>>>   }
>>>>>> @@ -1708,5 +1772,7 @@ end_branch_prob (void)
>>>>>>                (total_hist_br_prob[i] + total_hist_br_prob[19-i]) 
>>>>>> * 100
>>>>>>                / total_num_branches, 5*i, 5*i+5);
>>>>>>       }
>>>>>> +      fprintf (dump_file, "Total number of conditions: %d\n",
>>>>>> +           total_num_conds);
>>>>>>       }
>>>>>>   }
>>>>>> 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 00000000000..7c643c51a57
>>>>>> --- /dev/null
>>>>>> +++ b/gcc/testsuite/g++.dg/gcov/gcov-18.C
>>>>>> @@ -0,0 +1,258 @@
>>>>>> +/* { 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;
>>>>>> +}
>>>>>> +
>>>>>> +
>>>>>> +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);
>>>>>> +}
>>>>>> +
>>>>>> +/* { 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 00000000000..17f1fb4e923
>>>>>> --- /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 00000000000..215faffc980
>>>>>> --- /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 00000000000..3395422166a
>>>>>> --- /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 00000000000..641791a7223
>>>>>> --- /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 00000000000..72849d80e3a
>>>>>> --- /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 e5e94fa5a76..5a3e20ce374 100644
>>>>>> --- a/gcc/testsuite/lib/gcov.exp
>>>>>> +++ b/gcc/testsuite/lib/gcov.exp
>>>>>> @@ -174,6 +174,252 @@ proc verify-branches { testname testcase 
>>>>>> file } {
>>>>>>       return $failed
>>>>>>   }
>>>>>> +#
>>>>>> +# 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
>>>>>>   #
>>>>>> @@ -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
>>>>>>       }
>>>>>> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
>>>>>> index 65e51b939a2..44b9fa10431 100644
>>>>>> --- a/gcc/tree-core.h
>>>>>> +++ b/gcc/tree-core.h
>>>>>> @@ -1582,6 +1582,9 @@ enum omp_clause_linear_kind
>>>>>>   struct GTY(()) tree_exp {
>>>>>>     struct tree_typed typed;
>>>>>>     location_t locus;
>>>>>> +  /* Discriminator for basic conditions in a Boolean expressions. 
>>>>>> Trees that
>>>>>> +     are operands of the same Boolean expression should have the 
>>>>>> same uid.  */
>>>>>> +  unsigned condition_uid;
>>>>>>     tree GTY ((length ("TREE_OPERAND_LENGTH ((tree)&%h)"))) 
>>>>>> operands[1];
>>>>>>   };
>>>>>> diff --git a/gcc/tree-profile.cc b/gcc/tree-profile.cc
>>>>>> index 9c8fdb8b18f..f19dd3840e0 100644
>>>>>> --- a/gcc/tree-profile.cc
>>>>>> +++ b/gcc/tree-profile.cc
>>>>>> @@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
>>>>>>   #include "alloc-pool.h"
>>>>>>   #include "symbol-summary.h"
>>>>>>   #include "symtab-thunks.h"
>>>>>> +#include "cfganal.h"
>>>>>>   static GTY(()) tree gcov_type_node;
>>>>>>   static GTY(()) tree tree_interval_profiler_fn;
>>>>>> @@ -108,6 +109,1054 @@ enum counter_update_method {
>>>>>>   static counter_update_method counter_update = 
>>>>>> COUNTER_UPDATE_SINGLE_THREAD;
>>>>>> +/* These functions support measuring modified 
>>>>>> conditition/decision coverage
>>>>>> +   (MC/DC).  MC/DC requires all of the below during testing:
>>>>>> +
>>>>>> +   - Each entry and exit point is invoked
>>>>>> +   - Each decision takes every possible outcome
>>>>>> +   - Each condition in a decision takes every possible outcome
>>>>>> +   - Each condition in a decision is shown to independently 
>>>>>> affect the outcome
>>>>>> +     of the decision
>>>>>> +
>>>>>> +   Independence of a condition is shown by proving that only one 
>>>>>> condition
>>>>>> +   changes at a time.  This feature adds some instrumentation 
>>>>>> code, a few
>>>>>> +   bitwise operators, that records the branches taken in 
>>>>>> conditions and applies
>>>>>> +   a filter for the masking effect.  Masking is essentially 
>>>>>> short-circuiting in
>>>>>> +   reverse: a condition does not contribute to the outcome if it 
>>>>>> would short
>>>>>> +   circuit the (sub) expression if it was evaluated 
>>>>>> right-to-left, (_ && false)
>>>>>> +   and (_ || true).
>>>>>> +
>>>>>> +   The program is essentially rewritten this way:
>>>>>> +
>>>>>> +   - if (a || b) { fn () }
>>>>>> +   + if (a) { _t |= 0x1; goto _then; }
>>>>>> +   + else   { _f |= 0x1;
>>>>>> +   +    if (b) { _t |= 0x2; _mask |= 0x1; goto _then; }
>>>>>> +   +    else   { _f |= 0x2; goto _else; }
>>>>>> +   + _then:
>>>>>> +   + _gcov_t |= (_t & _mask);
>>>>>> +   + _gcov_f |= (_f & _mask);
>>>>>> +   + fn (); goto _end;
>>>>>> +   + _else:
>>>>>> +   + _gcov_t |= (_t & _mask);
>>>>>> +   + _gcov_f |= (_f & _mask);
>>>>>> +   + fn ();
>>>>>> +   + _end:
>>>>>> +
>>>>>> +   It is assumed the front end will provide discrimnators so that 
>>>>>> conditional
>>>>>> +   basic blocks (basic block with a conditional jump and outgoing 
>>>>>> true/false
>>>>>> +   edges) that belong to the same Boolean expression have the same
>>>>>> +   discriminator.  Masking is determined by analyzing these 
>>>>>> expressions as a
>>>>>> +   reduced order binary decision diagram.  */
>>>>>> +namespace
>>>>>> +{
>>>>>> +/* Some context and reused instances between function calls. 
>>>>>> Large embedded
>>>>>> +   buffers are used to up-front request enough memory for most 
>>>>>> programs and
>>>>>> +   merge them into a single allocation at the cost of using more 
>>>>>> memory in the
>>>>>> +   average case.  Some numbers from linux v5.13 which is assumed 
>>>>>> to be a
>>>>>> +   reasonably diverse code base: 75% of the functions in linux 
>>>>>> have less than
>>>>>> +   16 nodes in the CFG and approx 2.5% have more than 64 nodes. 
>>>>>> The functions
>>>>>> +   that go beyond a few dozen nodes tend to be very large (>100) 
>>>>>> and so 64
>>>>>> +   seems like a good balance.
>>>>>> +
>>>>>> +   This is really just a performance balance of the cost of 
>>>>>> allocation and
>>>>>> +   wasted memory.  */
>>>>>> +struct conds_ctx
>>>>>> +{
>>>>>> +    /* This is both a reusable shared allocation which is also 
>>>>>> used to return
>>>>>> +       single expressions, which means it for most code should 
>>>>>> only hold a
>>>>>> +       couple of elements.  */
>>>>>> +    auto_vec<basic_block, 64> blocks;
>>>>>> +
>>>>>> +    /* Index for the topological order indexed by 
>>>>>> basic_block->index to an
>>>>>> +       ordering so that expression (a || b && c) => top_index[a] 
>>>>>> < top_index[b]
>>>>>> +       < top_index[c].  */
>>>>>> +    auto_vec<int, 256> top_index;
>>>>>> +
>>>>>> +    /* Pre-allocate bitmaps and vectors for per-function book 
>>>>>> keeping.  This is
>>>>>> +       pure instance reuse and the bitmaps carry no data between 
>>>>>> function
>>>>>> +       calls.  */
>>>>>> +    auto_vec<basic_block, 64> B1;
>>>>>> +    auto_vec<basic_block, 64> B2;
>>>>>> +    auto_sbitmap G1;
>>>>>> +    auto_sbitmap G2;
>>>>>> +    auto_sbitmap G3;
>>>>>> +
>>>>>> +    explicit conds_ctx (unsigned size) noexcept (true) : G1 
>>>>>> (size), G2 (size),
>>>>>> +    G3 (size)
>>>>>> +    {
>>>>>> +    }
>>>>>> +};
>>>>>> +
>>>>>> +/* Only instrument terms with fewer than number of bits in a 
>>>>>> (wide) gcov
>>>>>> +   integer, which is probably 64.  The algorithm itself does not 
>>>>>> impose this
>>>>>> +   limitation, but it makes for a simpler implementation.
>>>>>> +
>>>>>> +   * Allocating the output data structure (coverage_counter_alloc 
>>>>>> ()) can
>>>>>> +     assume pairs of gcov_type_unsigned and not use a separate 
>>>>>> length field.
>>>>>> +   * A pair gcov_type_unsigned can be used as accumulators.
>>>>>> +   * Updating accumulators is can use the bitwise operations |=, 
>>>>>> &= and not
>>>>>> +     custom operators that work for arbitrary-sized bit-sets.
>>>>>> +
>>>>>> +   Most real-world code should be unaffected by this, but it is 
>>>>>> possible
>>>>>> +   (especially for generated code) to exceed this limit.  */
>>>>>> +#define CONDITIONS_MAX_TERMS (TYPE_PRECISION (gcov_type_node))
>>>>>> +#define EDGE_CONDITION (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)
>>>>>> +
>>>>>> +/* Compare two basic blocks by their order in the expression i.e. 
>>>>>> for (a || b)
>>>>>> +   then topological_cmp (a, b, ...) < 0.  The result is undefined 
>>>>>> if lhs, rhs
>>>>>> +   belong to different expressions.  The top_index argument 
>>>>>> should be the
>>>>>> +   top_index vector from ctx.  */
>>>>>> +int
>>>>>> +topological_cmp (const void *lhs, const void *rhs, void *top_index)
>>>>>> +{
>>>>>> +    const_basic_block l = *(const basic_block*) lhs;
>>>>>> +    const_basic_block r = *(const basic_block*) rhs;
>>>>>> +    const vec<int>* im = (const vec<int>*) top_index;
>>>>>> +    return (*im)[l->index] - (*im)[r->index];
>>>>>> +}
>>>>>> +
>>>>>> +/* Find the index of needle in blocks; return -1 if not found. 
>>>>>> This has two
>>>>>> +   uses, sometimes for the index and sometimes for set member c 
>>>>>> hecks.  Sets are
>>>>>> +   typically very small (number of conditions, >8 is uncommon) so 
>>>>>> linear search
>>>>>> +   should be very fast.  */
>>>>>> +int
>>>>>> +index_of (const basic_block needle, array_slice<basic_block> blocks)
>>>>>> +{
>>>>>> +    for (size_t i = 0; i < blocks.size (); i++)
>>>>>> +    if (blocks[i] == needle)
>>>>>> +        return int (i);
>>>>>> +    return -1;
>>>>>> +}
>>>>>> +
>>>>>> +/* Special cases of the single_*_p and single_*_edge functions in 
>>>>>> basic-block.h
>>>>>> +   that don't consider exception handling or other complex edges. 
>>>>>> This helps
>>>>>> +   create a view of the CFG with only normal edges - if a basic 
>>>>>> block has both
>>>>>> +   an outgoing fallthrough and exceptional edge, it should be 
>>>>>> considered a
>>>>>> +   single-successor.  */
>>>>>> +bool
>>>>>> +single_p (const vec<edge, va_gc> *edges)
>>>>>> +{
>>>>>> +    int n = EDGE_COUNT (edges);
>>>>>> +    if (n == 0)
>>>>>> +    return false;
>>>>>> +
>>>>>> +    for (edge e : edges)
>>>>>> +    if (e->flags & EDGE_COMPLEX)
>>>>>> +        n -= 1;
>>>>>> +
>>>>>> +    return n == 1;
>>>>>> +}
>>>>>> +
>>>>>> +/* Get the single, non-complex edge.  Behavior is undefined edges 
>>>>>> have more
>>>>>> +   than 1 non-complex edges.  */
>>>>>> +edge
>>>>>> +single_edge (const vec<edge, va_gc> *edges)
>>>>>> +{
>>>>>> +    gcc_checking_assert (single_p (edges));
>>>>>> +    for (edge e : edges)
>>>>>> +    {
>>>>>> +    if (e->flags & EDGE_COMPLEX)
>>>>>> +        continue;
>>>>>> +    return e;
>>>>>> +    }
>>>>>> +    return NULL;
>>>>>> +}
>>>>>> +
>>>>>> +/* Sometimes, for example with function calls, goto labels, and C++
>>>>>> +   destructors, the CFG gets extra nodes that are essentially 
>>>>>> single-entry
>>>>>> +   single-exit in the middle of boolean expressions.  For example:
>>>>>> +
>>>>>> +   x || can_throw (y)
>>>>>> +
>>>>>> +         A
>>>>>> +        /|
>>>>>> +       / |
>>>>>> +      B  |
>>>>>> +      |  |
>>>>>> +      C  |
>>>>>> +     / \ |
>>>>>> +    /   \|
>>>>>> +   F     T
>>>>>> +
>>>>>> +   Without the extra node inserted by the function + exception it 
>>>>>> becomes a
>>>>>> +   proper 2-term graph, not 2 single-term graphs.
>>>>>> +
>>>>>> +       A
>>>>>> +      /|
>>>>>> +     C |
>>>>>> +    / \|
>>>>>> +   F   T
>>>>>> +
>>>>>> +   This function finds the source edge of these paths.  This is 
>>>>>> often the
>>>>>> +   identity function.  */
>>>>>> +edge
>>>>>> +contract_edge_up (edge e)
>>>>>> +{
>>>>>> +    while (true)
>>>>>> +    {
>>>>>> +    basic_block src = e->src;
>>>>>> +    if (!single_p (src->preds))
>>>>>> +        return e;
>>>>>> +    if (!single_p (src->succs))
>>>>>> +        return e;
>>>>>> +    e = single_edge (src->preds);
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>> +/* A simple struct for storing/returning outcome block pairs. 
>>>>>> Either both
>>>>>> +   blocks are set or both are NULL.  */
>>>>>> +struct outcomes
>>>>>> +{
>>>>>> +    basic_block t = NULL;
>>>>>> +    basic_block f = NULL;
>>>>>> +
>>>>>> +    operator bool () const noexcept (true)
>>>>>> +    {
>>>>>> +    return t && f;
>>>>>> +    }
>>>>>> +};
>>>>>> +
>>>>>> +/* Get the true/false successors of a basic block.  If b is not a 
>>>>>> conditional
>>>>>> +   block both edges are NULL.  */
>>>>>> +outcomes
>>>>>> +conditional_succs (const basic_block b)
>>>>>> +{
>>>>>> +    outcomes c;
>>>>>> +    for (edge e : b->succs)
>>>>>> +    {
>>>>>> +    if (e->flags & EDGE_TRUE_VALUE)
>>>>>> +        c.t = e->dest;
>>>>>> +    if (e->flags & EDGE_FALSE_VALUE)
>>>>>> +        c.f = e->dest;
>>>>>> +    }
>>>>>> +
>>>>>> +    gcc_assert ((c.t && c.f) || (!c.t && !c.f));
>>>>>> +    return c;
>>>>>> +}
>>>>>> +
>>>>>> +/* Get the index or offset of a conditional flag, 0 for true and 
>>>>>> 1 for false.
>>>>>> +   These indices carry no semantics but must be consistent as 
>>>>>> they are used to
>>>>>> +   index into data structures in code generation and gcov.  */
>>>>>> +unsigned
>>>>>> +condition_index (unsigned flag)
>>>>>> +{
>>>>>> +    return (flag & EDGE_CONDITION) == EDGE_TRUE_VALUE ? 0 : 1;
>>>>>> +}
>>>>>> +
>>>>>> +/* Returns the condition identifier for the basic block if set, 
>>>>>> otherwise 0.
>>>>>> +   This is only meaningful in GIMPLE and is used for condition 
>>>>>> coverage.
>>>>>> +
>>>>>> +   There may be conditions created that did not get an uid, such 
>>>>>> as those
>>>>>> +   implicitly created by destructors.  We could include them in 
>>>>>> the condition
>>>>>> +   coverage for completeness (i.e. condition coverage implies 
>>>>>> (implicit) branch
>>>>>> +   coverage), but they have no natural buckets and should all be 
>>>>>> single-term.
>>>>>> +   For now these are ignored and given uid = 0, and branch 
>>>>>> coverage is left to
>>>>>> +   -fprofile-arcs.
>>>>>> +
>>>>>> +   Under optimization, COND_EXPRs may be folded, replaced with 
>>>>>> switches,
>>>>>> +   min-max, etc., which leaves ghost identifiers in basic blocks 
>>>>>> that do not
>>>>>> +   end with a conditional jump.  They are not really meaningful 
>>>>>> for condition
>>>>>> +   coverage anymore, but since coverage is unreliable under 
>>>>>> optimization anyway
>>>>>> +   this is not a big problem.  */
>>>>>> +unsigned
>>>>>> +condition_uid (struct function *fn, basic_block b)
>>>>>> +{
>>>>>> +    gimple *stmt = gsi_stmt (gsi_last_bb (b));
>>>>>> +    if (!safe_is_a<gcond *> (stmt))
>>>>>> +    return 0;
>>>>>> +
>>>>>> +    unsigned *v = fn->cond_uids->get (as_a <gcond*> (stmt));
>>>>>> +    return v ? *v : 0;
>>>>>> +}
>>>>>> +
>>>>>> +/* Compute the masking vector.
>>>>>> +
>>>>>> +   Masking and short circuiting are deeply connected - masking 
>>>>>> occurs when
>>>>>> +   control flow reaches a state that is also reachable with short 
>>>>>> circuiting.
>>>>>> +   In fact, masking corresponds to short circuiting for the reversed
>>>>>> +   expression.  This means we can find the limits, the last term 
>>>>>> in preceeding
>>>>>> +   subexpressions, by following the edges that short circuit to 
>>>>>> the same
>>>>>> +   outcome.  The algorithm treats the CFG as a reduced order 
>>>>>> binary decision
>>>>>> +   diagram (see Randall E. Bryant's Graph Based Algorithms for 
>>>>>> Boolean
>>>>>> +   Function Manipulation (1987)).
>>>>>> +
>>>>>> +   In the simplest case a || b:
>>>>>> +
>>>>>> +   a
>>>>>> +   |\
>>>>>> +   | b
>>>>>> +   |/ \
>>>>>> +   T   F
>>>>>> +
>>>>>> +   T has has multiple incoming edges and is the outcome of a 
>>>>>> short circuit,
>>>>>> +   with top = a, bot = b.  The top node (a) is masked when the 
>>>>>> edge (b, T) is
>>>>>> +   taken.
>>>>>> +
>>>>>> +   The names "top" and "bot" refer to a pair of nodes with a shared
>>>>>> +   successor.  The top is always the node corresponding to the 
>>>>>> left-most
>>>>>> +   operand of the two, and it holds that top < bot in a 
>>>>>> topological ordering.
>>>>>> +
>>>>>> +   Now consider (a && b) || (c && d) and its masking vectors:
>>>>>> +
>>>>>> +   a
>>>>>> +   |\
>>>>>> +   b \
>>>>>> +   |\|
>>>>>> +   | c
>>>>>> +   | |\
>>>>>> +   | d \
>>>>>> +   |/ \|
>>>>>> +   T   F
>>>>>> +
>>>>>> +   a[0] = {}
>>>>>> +   a[1] = {}
>>>>>> +   b[0] = {a}
>>>>>> +   b[1] = {}
>>>>>> +   c[0] = {}
>>>>>> +   c[1] = {}
>>>>>> +   d[0] = {c}
>>>>>> +   d[1] = {a,b}
>>>>>> +
>>>>>> +   Note that 0 and 1 are indices and not boolean values - a[0] is 
>>>>>> the index in
>>>>>> +   the masking vector when a takes the true edge.
>>>>>> +
>>>>>> +   b[0] and d[0] are identical to the a || b example, and d[1] is 
>>>>>> the bot in
>>>>>> +   the triangle [d, b] -> T.  b is the top node in the [d, b] 
>>>>>> relationship and
>>>>>> +   last term in (a && b).  To find the other terms masked we use 
>>>>>> the fact that
>>>>>> +   all paths in an expression go through either of the outcomes, 
>>>>>> found by
>>>>>> +   collecting all non-complex edges that go out of the expression 
>>>>>> (the
>>>>>> +   neighborhood).  In some cases the outgoing edge go through 
>>>>>> intermediate (or
>>>>>> +   bypass) nodes, and we collect these paths too (see 
>>>>>> contract_edge_up).
>>>>>> +
>>>>>> +   We find the terms by marking the outcomes (in this case c, T) 
>>>>>> and walk the
>>>>>> +   predecessors starting at top (in this case b) and masking 
>>>>>> nodes when both
>>>>>> +   successors are marked.
>>>>>> +
>>>>>> +   The masking vector is represented as two bitfields per term in 
>>>>>> the
>>>>>> +   expression with the index corresponding to the term in the source
>>>>>> +   expression.  a || b && c becomes the term vector [a b c] and 
>>>>>> the masking
>>>>>> +   vectors [a[0] a[1] b[0] ...].  The kth bit of a masking vector 
>>>>>> is set if the
>>>>>> +   the kth term is masked by taking the edge.
>>>>>> +
>>>>>> +   The out masks are in uint64_t (the practical maximum for 
>>>>>> gcov_type_node for
>>>>>> +   any target) as it has to be big enough to store the target 
>>>>>> size gcov types
>>>>>> +   independent of the host.  */
>>>>>> +void
>>>>>> +masking_vectors (conds_ctx& ctx, array_slice<basic_block> blocks,
>>>>>> +         array_slice<sbitmap> maps, array_slice<uint64_t> masks)
>>>>>> +{
>>>>>> +    gcc_assert (blocks.is_valid ());
>>>>>> +    gcc_assert (!blocks.empty ());
>>>>>> +    gcc_assert (maps.is_valid ());
>>>>>> +    gcc_assert (masks.is_valid ());
>>>>>> +    gcc_assert (sizeof (masks[0]) * BITS_PER_UNIT >= 
>>>>>> CONDITIONS_MAX_TERMS);
>>>>>> +
>>>>>> +    if (bitmap_count_bits (maps[0]) == 1)
>>>>>> +    return;
>>>>>> +
>>>>>> +    sbitmap marks = ctx.G1;
>>>>>> +    const sbitmap core = maps[0];
>>>>>> +    const sbitmap allg = maps[1];
>>>>>> +    vec<basic_block>& queue = ctx.B1;
>>>>>> +    vec<basic_block>& body = ctx.B2;
>>>>>> +    const vec<int>& top_index = ctx.top_index;
>>>>>> +
>>>>>> +    /* Set up for the iteration - include the outcome nodes in 
>>>>>> the traversal.
>>>>>> +       The algorithm compares pairs of nodes and is not really 
>>>>>> sensitive to
>>>>>> +       traversal order, but need to maintain topological order 
>>>>>> because the
>>>>>> +       index of masking nodes maps to the index in the 
>>>>>> accumulators. We must
>>>>>> +       also check the incoming-to-outcome pairs.  These edges may 
>>>>>> in turn be
>>>>>> +       split (this happens with labels on top of then/else 
>>>>>> blocks) so we must
>>>>>> +       follow any single-in single-out path.  The non-condition 
>>>>>> blocks do not
>>>>>> +       have to be in order as they are non-condition blocks and 
>>>>>> will not be
>>>>>> +       considered for the set-bit index.  */
>>>>>> +    body.truncate (0);
>>>>>> +    body.reserve (blocks.size () + 2);
>>>>>> +    for (const basic_block b : blocks)
>>>>>> +    if (bitmap_bit_p (core, b->index))
>>>>>> +        body.quick_push (b);
>>>>>> +
>>>>>> +    for (basic_block b : blocks)
>>>>>> +    {
>>>>>> +    if (!bitmap_bit_p (core, b->index))
>>>>>> +        continue;
>>>>>> +
>>>>>> +    for (edge e : b->succs)
>>>>>> +    {
>>>>>> +        if (e->flags & EDGE_COMPLEX)
>>>>>> +        continue;
>>>>>> +        if (bitmap_bit_p (allg, e->dest->index))
>>>>>> +        continue;
>>>>>> +        body.safe_push (e->dest);
>>>>>> +
>>>>>> +        /* There may be multiple nodes between the condition edge 
>>>>>> and the
>>>>>> +           actual outcome, and we need to know when these paths 
>>>>>> join to
>>>>>> +           determine if there is short circuit/masking.  This is
>>>>>> +           effectively creating a virtual edge from the condition 
>>>>>> node to
>>>>>> +           the real outcome.  */
>>>>>> +        while (!(e->flags & EDGE_DFS_BACK) && single_p 
>>>>>> (e->dest->succs))
>>>>>> +        {
>>>>>> +        e = single_edge (e->dest->succs);
>>>>>> +        body.safe_push (e->dest);
>>>>>> +        }
>>>>>> +    }
>>>>>> +    }
>>>>>> +
>>>>>> +    /* Find the masking.  The leftmost element cannot mask 
>>>>>> anything, so
>>>>>> +       start at 1.  */
>>>>>> +    for (size_t i = 1; i != body.length (); i++)
>>>>>> +    {
>>>>>> +    const basic_block b = body[i];
>>>>>> +    for (edge e1 : b->preds)
>>>>>> +    for (edge e2 : b->preds)
>>>>>> +    {
>>>>>> +        if (e1 == e2)
>>>>>> +        continue;
>>>>>> +        if ((e1->flags | e2->flags) & EDGE_COMPLEX)
>>>>>> +        continue;
>>>>>> +
>>>>>> +        edge etop = contract_edge_up (e1);
>>>>>> +        edge ebot = contract_edge_up (e2);
>>>>>> +        gcc_assert (etop != ebot);
>>>>>> +
>>>>>> +        const basic_block top = etop->src;
>>>>>> +        const basic_block bot = ebot->src;
>>>>>> +        const unsigned cond = etop->flags & ebot->flags & 
>>>>>> EDGE_CONDITION;
>>>>>> +        if (!cond)
>>>>>> +        continue;
>>>>>> +        if (top_index[top->index] > top_index[bot->index])
>>>>>> +        continue;
>>>>>> +        if (!bitmap_bit_p (core, top->index))
>>>>>> +        continue;
>>>>>> +        if (!bitmap_bit_p (core, bot->index))
>>>>>> +        continue;
>>>>>> +
>>>>>> +        outcomes out = conditional_succs (top);
>>>>>> +        gcc_assert (out);
>>>>>> +        bitmap_clear (marks);
>>>>>> +        bitmap_set_bit (marks, out.t->index);
>>>>>> +        bitmap_set_bit (marks, out.f->index);
>>>>>> +        queue.truncate (0);
>>>>>> +        queue.safe_push (top);
>>>>>> +
>>>>>> +        // The edge bot -> outcome triggers the masking
>>>>>> +        const int m = 2*index_of (bot, body) + condition_index 
>>>>>> (cond);
>>>>>> +        gcc_assert (m >= 0);
>>>>>> +        while (!queue.is_empty ())
>>>>>> +        {
>>>>>> +        basic_block q = queue.pop ();
>>>>>> +        /* q may have been processed & completed by being added 
>>>>>> to the
>>>>>> +           queue multiple times, so check that there is still 
>>>>>> work to
>>>>>> +           do before continuing.  */
>>>>>> +        if (bitmap_bit_p (marks, q->index))
>>>>>> +            continue;
>>>>>> +
>>>>>> +        outcomes succs = conditional_succs (q);
>>>>>> +        if (!bitmap_bit_p (marks, succs.t->index))
>>>>>> +            continue;
>>>>>> +        if (!bitmap_bit_p (marks, succs.f->index))
>>>>>> +            continue;
>>>>>> +
>>>>>> +        const int index = index_of (q, body);
>>>>>> +        gcc_assert (index != -1);
>>>>>> +        masks[m] |= uint64_t (1) << index;
>>>>>> +        bitmap_set_bit (marks, q->index);
>>>>>> +
>>>>>> +        for (edge e : q->preds)
>>>>>> +        {
>>>>>> +            e = contract_edge_up (e);
>>>>>> +            if (e->flags & EDGE_DFS_BACK)
>>>>>> +            continue;
>>>>>> +            if (bitmap_bit_p (marks, e->src->index))
>>>>>> +            continue;
>>>>>> +            if (!bitmap_bit_p (core, e->src->index))
>>>>>> +            continue;
>>>>>> +            queue.safe_push (e->src);
>>>>>> +        }
>>>>>> +        }
>>>>>> +    }
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>> +/* Emit <lhs> = <rhs> on edges.  This is just a short hand that 
>>>>>> automates the
>>>>>> +   building of the assign and immediately puts it on the edge, 
>>>>>> which becomes
>>>>>> +   noisy.  */
>>>>>> +tree
>>>>>> +emit_assign (edge e, tree lhs, tree rhs)
>>>>>> +{
>>>>>> +    gassign *w = gimple_build_assign (lhs, rhs);
>>>>>> +    gsi_insert_on_edge (e, w);
>>>>>> +    return lhs;
>>>>>> +}
>>>>>> +
>>>>>> +/* Emit lhs = <rhs> on edges.  */
>>>>>> +tree
>>>>>> +emit_assign (edge e, tree rhs)
>>>>>> +{
>>>>>> +    return emit_assign (e, make_ssa_name (gcov_type_node), rhs);
>>>>>> +}
>>>>>> +
>>>>>> +/* Emit lhs = op1 <op> op2 on edges.  */
>>>>>> +tree
>>>>>> +emit_bitwise_op (edge e, tree op1, tree_code op, tree op2 = 
>>>>>> NULL_TREE)
>>>>>> +{
>>>>>> +    tree lhs = make_ssa_name (gcov_type_node);
>>>>>> +    gassign *w = gimple_build_assign (lhs, op, op1, op2);
>>>>>> +    gsi_insert_on_edge (e, w);
>>>>>> +    return lhs;
>>>>>> +}
>>>>>> +
>>>>>> +/* Visitor for make_top_index.  */
>>>>>> +void
>>>>>> +make_top_index_visit (basic_block b, vec<basic_block>& L, 
>>>>>> vec<int>& marks)
>>>>>> +{
>>>>>> +    if (marks[b->index])
>>>>>> +    return;
>>>>>> +
>>>>>> +    /* Follow the false edge first, if it exists, so that true 
>>>>>> paths are given
>>>>>> +       the lower index in the ordering.  Any iteration order
>>>>>> +       would yield a valid and useful topological ordering, but 
>>>>>> making sure the
>>>>>> +       true branch has the lower index first makes reporting work 
>>>>>> better for
>>>>>> +       expressions with ternaries.  Walk the false branch first 
>>>>>> because the
>>>>>> +       array will be reversed to finalize the topological order.
>>>>>> +
>>>>>> +       With the wrong ordering (a ? b : c) && d could become [a c 
>>>>>> b d], but the
>>>>>> +       (expected) order is really [a b c d].  */
>>>>>> +
>>>>>> +    const unsigned false_fwd = EDGE_DFS_BACK | EDGE_FALSE_VALUE;
>>>>>> +    for (edge e : b->succs)
>>>>>> +    if ((e->flags & false_fwd) == EDGE_FALSE_VALUE)
>>>>>> +        make_top_index_visit (e->dest, L, marks);
>>>>>> +
>>>>>> +    for (edge e : b->succs)
>>>>>> +    if (!(e->flags & false_fwd))
>>>>>> +        make_top_index_visit (e->dest, L, marks);
>>>>>> +
>>>>>> +    marks[b->index] = 1;
>>>>>> +    L.quick_push (b);
>>>>>> +}
>>>>>> +
>>>>>> +/* Find a topological sorting of the blocks in a function so that 
>>>>>> left operands
>>>>>> +   are before right operands including subexpressions.  Sorting 
>>>>>> on block index
>>>>>> +   does not guarantee this property and the syntactical order of 
>>>>>> terms is very
>>>>>> +   important to the condition coverage.  The sorting algorithm is 
>>>>>> from Cormen
>>>>>> +   et al (2001) but with back-edges ignored and thus there is no 
>>>>>> need for
>>>>>> +   temporary marks (for cycle detection).  The L argument is a 
>>>>>> buffer/working
>>>>>> +   memory, and the output will be written to top_index.
>>>>>> +
>>>>>> +   For the expression (a || (b && c) || d) the blocks should be 
>>>>>> [a b c d].  */
>>>>>> +void
>>>>>> +make_top_index (array_slice<basic_block> blocks, 
>>>>>> vec<basic_block>& L,
>>>>>> +        vec<int>& top_index)
>>>>>> +{
>>>>>> +    L.truncate (0);
>>>>>> +    L.reserve (blocks.size ());
>>>>>> +
>>>>>> +    /* Use of the output map as a temporary for tracking visited 
>>>>>> status.  */
>>>>>> +    top_index.truncate (0);
>>>>>> +    top_index.safe_grow_cleared (blocks.size ());
>>>>>> +    for (const basic_block b : blocks)
>>>>>> +    make_top_index_visit (b, L, top_index);
>>>>>> +
>>>>>> +    /* Insert canaries - if there are unreachable nodes (for 
>>>>>> example infinite
>>>>>> +       loops) then the unreachable nodes should never be needed 
>>>>>> for comparison,
>>>>>> +       and L.length () < max_index.  An index mapping should also 
>>>>>> never be
>>>>>> +       recorded twice.  */
>>>>>> +    for (unsigned i = 0; i != top_index.length (); i++)
>>>>>> +    top_index[i] = -1;
>>>>>> +
>>>>>> +    gcc_assert (blocks.size () == L.length ());
>>>>>> +    L.reverse ();
>>>>>> +    const unsigned nblocks = L.length ();
>>>>>> +    for (unsigned i = 0; i != nblocks; i++)
>>>>>> +    {
>>>>>> +    gcc_assert (L[i]->index != -1);
>>>>>> +    top_index[L[i]->index] = int (i);
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>> +/* Find all nodes including non-conditions in a Boolean 
>>>>>> expression. We need to
>>>>>> +   know the paths through the expression so that the masking and
>>>>>> +   instrumentation phases can limit searches and know what 
>>>>>> subgraphs must be
>>>>>> +   threaded through, but not counted, such as the (b || c) in
>>>>>> +   a && fn (b || c) && d.
>>>>>> +
>>>>>> +   It is essentially the intersection of downwards paths from the 
>>>>>> expression
>>>>>> +   nodices to the post-dominator and upwards from the 
>>>>>> post-dominator.  Finding
>>>>>> +   the dominator is slightly more involved than picking the 
>>>>>> first/last,
>>>>>> +   particularly under optimization, because both incoming and 
>>>>>> outgoing paths
>>>>>> +   may have multiple entries/exits.
>>>>>> +
>>>>>> +   It is assumed the graph is an array_slice to the basic blocks 
>>>>>> of this
>>>>>> +   function sorted by the basic block index.  */
>>>>>> +vec<basic_block>&
>>>>>> +paths_between (conds_ctx &ctx, array_slice<basic_block> graph,
>>>>>> +           const vec<basic_block>& expr)
>>>>>> +{
>>>>>> +    if (expr.length () == 1)
>>>>>> +    {
>>>>>> +    ctx.blocks.truncate (0);
>>>>>> +    ctx.blocks.safe_push (expr[0]);
>>>>>> +    return ctx.blocks;
>>>>>> +    }
>>>>>> +
>>>>>> +    basic_block dom;
>>>>>> +    sbitmap up = ctx.G1;
>>>>>> +    sbitmap down = ctx.G2;
>>>>>> +    sbitmap paths = ctx.G3;
>>>>>> +    vec<basic_block>& queue = ctx.B1;
>>>>>> +
>>>>>> +    queue.truncate (0);
>>>>>> +    bitmap_clear (down);
>>>>>> +    dom = get_immediate_dominator (CDI_POST_DOMINATORS, expr[0]);
>>>>>> +    for (basic_block b : expr)
>>>>>> +    if (dom != b)
>>>>>> +        dom = nearest_common_dominator (CDI_POST_DOMINATORS, dom, 
>>>>>> b);
>>>>>> +    queue.safe_splice (expr);
>>>>>> +    while (!queue.is_empty ())
>>>>>> +    {
>>>>>> +    basic_block b = queue.pop ();
>>>>>> +    if (!bitmap_set_bit (down, b->index))
>>>>>> +        continue;
>>>>>> +    if (b == dom)
>>>>>> +        continue;
>>>>>> +    for (edge e : b->succs)
>>>>>> +        if (!(e->flags & (EDGE_COMPLEX | EDGE_DFS_BACK)))
>>>>>> +        queue.safe_push (e->dest);
>>>>>> +    }
>>>>>> +
>>>>>> +    queue.truncate (0);
>>>>>> +    bitmap_clear (up);
>>>>>> +    dom = expr[0];
>>>>>> +    for (basic_block b : expr)
>>>>>> +    if (dom != b)
>>>>>> +        dom = nearest_common_dominator (CDI_DOMINATORS, dom, b);
>>>>>> +    queue.safe_splice (expr);
>>>>>> +    while (!queue.is_empty ())
>>>>>> +    {
>>>>>> +    basic_block b = queue.pop ();
>>>>>> +    if (!bitmap_set_bit (up, b->index))
>>>>>> +        continue;
>>>>>> +    if (b == dom)
>>>>>> +        continue;
>>>>>> +    for (edge e : b->preds)
>>>>>> +        if (!(e->flags & (EDGE_COMPLEX | EDGE_DFS_BACK)))
>>>>>> +        queue.safe_push (e->src);
>>>>>> +    }
>>>>>> +
>>>>>> +    bitmap_and (paths, up, down);
>>>>>> +    vec<basic_block>& blocks = ctx.blocks;
>>>>>> +    blocks.truncate (0);
>>>>>> +    blocks.reserve (graph.size ());
>>>>>> +    sbitmap_iterator itr;
>>>>>> +    unsigned index;
>>>>>> +    EXECUTE_IF_SET_IN_BITMAP (paths, 0, index, itr)
>>>>>> +    blocks.quick_push (graph[index]);
>>>>>> +    return blocks;
>>>>>> +}
>>>>>> +
>>>>>> +}
>>>>>> +
>>>>>> +/* Context object for the condition coverage.  This stores 
>>>>>> conds_ctx (the
>>>>>> +   buffers reused when analyzing the cfg) and the output arrays. 
>>>>>> This is
>>>>>> +   designed to be heap allocated and aggressively preallocates 
>>>>>> large buffers to
>>>>>> +   avoid having to reallocate for most programs.  */
>>>>>> +struct condcov
>>>>>> +{
>>>>>> +    explicit condcov (unsigned nblocks) noexcept (true) : ctx 
>>>>>> (nblocks),
>>>>>> +    m_maps (sbitmap_vector_alloc (2 * nblocks, nblocks))
>>>>>> +    {
>>>>>> +    bitmap_vector_clear (m_maps, 2 * nblocks);
>>>>>> +    }
>>>>>> +    auto_vec<size_t, 128> m_index;
>>>>>> +    auto_vec<basic_block, 256> m_blocks;
>>>>>> +    auto_vec<uint64_t, 512> m_masks;
>>>>>> +    conds_ctx ctx;
>>>>>> +    sbitmap *m_maps;
>>>>>> +};
>>>>>> +
>>>>>> +/* Get the length, that is the number of Boolean expression 
>>>>>> found. cov_length
>>>>>> +   is the one-past index for cov_{blocks,masks,maps}.  */
>>>>>> +size_t
>>>>>> +cov_length (const struct condcov* cov)
>>>>>> +{
>>>>>> +    if (cov->m_index.is_empty ())
>>>>>> +    return 0;
>>>>>> +    return cov->m_index.length () - 1;
>>>>>> +}
>>>>>> +
>>>>>> +/* The subgraph, exluding intermediates, for the nth Boolean 
>>>>>> expression.  */
>>>>>> +array_slice<basic_block>
>>>>>> +cov_blocks (struct condcov* cov, size_t n)
>>>>>> +{
>>>>>> +    if (n >= cov->m_index.length ())
>>>>>> +    return array_slice<basic_block>::invalid ();
>>>>>> +
>>>>>> +    basic_block *begin = cov->m_blocks.begin () + cov->m_index[n];
>>>>>> +    basic_block *end = cov->m_blocks.begin () + cov->m_index[n + 1];
>>>>>> +    return array_slice<basic_block> (begin, end - begin);
>>>>>> +}
>>>>>> +
>>>>>> +/* The masks for the nth Boolean expression.  */
>>>>>> +array_slice<uint64_t>
>>>>>> +cov_masks (struct condcov* cov, size_t n)
>>>>>> +{
>>>>>> +    if (n >= cov->m_index.length ())
>>>>>> +    return array_slice<uint64_t>::invalid ();
>>>>>> +
>>>>>> +    uint64_t *begin = cov->m_masks.begin () + 2*cov->m_index[n];
>>>>>> +    uint64_t *end = cov->m_masks.begin () + 2*cov->m_index[n + 1];
>>>>>> +    return array_slice<uint64_t> (begin, end - begin);
>>>>>> +}
>>>>>> +
>>>>>> +/* The maps for the nth Boolean expression.  */
>>>>>> +array_slice<sbitmap>
>>>>>> +cov_maps (struct condcov* cov, size_t n)
>>>>>> +{
>>>>>> +    if (n >= cov->m_index.length ())
>>>>>> +    return array_slice<sbitmap>::invalid ();
>>>>>> +
>>>>>> +    sbitmap *begin = cov->m_maps + 2*n;
>>>>>> +    sbitmap *end = begin + 2;
>>>>>> +    return array_slice<sbitmap> (begin, end - begin);
>>>>>> +}
>>>>>> +
>>>>>> +/* Deleter for condcov.  */
>>>>>> +void
>>>>>> +cov_free (struct condcov* cov)
>>>>>> +{
>>>>>> +    sbitmap_vector_free (cov->m_maps);
>>>>>> +    delete cov;
>>>>>> +}
>>>>>> +
>>>>>> +/* Condition coverage (MC/DC)
>>>>>> +
>>>>>> +   Whalen, Heimdahl, De Silva in "Efficient Test Coverage 
>>>>>> Measurement for
>>>>>> +   MC/DC" describe an algorithm for modified condition/decision 
>>>>>> coverage based
>>>>>> +   on AST analysis.  This algorithm does analyzes the control 
>>>>>> flow graph
>>>>>> +   (interpreted as a binary decision diagram) to determine the 
>>>>>> masking vectors.
>>>>>> +   The individual phases are described in more detail closer to the
>>>>>> +   implementation.
>>>>>> +
>>>>>> +   The coverage only considers the positions, not the symbols, in a
>>>>>> +   conditional, e.g. !A || (!B && A) is a 3-term conditional even 
>>>>>> though A
>>>>>> +   appears twice.  Subexpressions have no effect on term ordering:
>>>>>> +   (a && (b || (c && d)) || e) comes out as [a b c d e]. 
>>>>>> Functions whose
>>>>>> +   arguments are Boolean expressions are treated as separate 
>>>>>> expressions, that
>>>>>> +   is, a && fn (b || c) && d is treated as [a _fn d] and [b c], 
>>>>>> not [a b c d].
>>>>>> +
>>>>>> +   The output for gcov is a vector of pairs of unsigned integers, 
>>>>>> interpreted
>>>>>> +   as bit-sets, where the bit index corresponds to the index of 
>>>>>> the condition
>>>>>> +   in the expression.
>>>>>> +
>>>>>> +   The returned condcov should be released by the caller with 
>>>>>> cov_free.  */
>>>>>> +struct condcov*
>>>>>> +find_conditions (struct function *fn)
>>>>>> +{
>>>>>> +    mark_dfs_back_edges (fn);
>>>>>> +    const bool have_dom = dom_info_available_p (fn, CDI_DOMINATORS);
>>>>>> +    const bool have_post_dom = dom_info_available_p (fn, 
>>>>>> CDI_POST_DOMINATORS);
>>>>>> +    if (!have_dom)
>>>>>> +    calculate_dominance_info (CDI_DOMINATORS);
>>>>>> +    if (!have_post_dom)
>>>>>> +    calculate_dominance_info (CDI_POST_DOMINATORS);
>>>>>> +
>>>>>> +    const unsigned nblocks = n_basic_blocks_for_fn (fn);
>>>>>> +    basic_block *fnblocksp = basic_block_info_for_fn 
>>>>>> (fn)->address ();
>>>>>> +    condcov *cov = new condcov (nblocks);
>>>>>> +    conds_ctx& ctx = cov->ctx;
>>>>>> +    array_slice<basic_block> fnblocks (fnblocksp, nblocks);
>>>>>> +    make_top_index (fnblocks, ctx.B1, ctx.top_index);
>>>>>> +
>>>>>> +    /* Bin the Boolean expressions so that exprs[id] -> [x1, x2, 
>>>>>> ...].  */
>>>>>> +    hash_map<int_hash<unsigned, 0>, vec<basic_block>> exprs;
>>>>>> +    for (basic_block b : fnblocks)
>>>>>> +    {
>>>>>> +    const unsigned uid = condition_uid (fn, b);
>>>>>> +    if (uid == 0)
>>>>>> +        continue;
>>>>>> +    exprs.get_or_insert (uid).safe_push (b);
>>>>>> +    }
>>>>>> +
>>>>>> +    /* Visit all reachable nodes and collect conditions. 
>>>>>> Topological order is
>>>>>> +       important so the first node of a boolean expression is 
>>>>>> visited first
>>>>>> +       (it will mark subsequent terms).  */
>>>>>> +    cov->m_index.safe_push (0);
>>>>>> +    for (auto expr : exprs)
>>>>>> +    {
>>>>>> +    vec<basic_block>& conds = expr.second;
>>>>>> +    if (conds.length () > CONDITIONS_MAX_TERMS)
>>>>>> +    {
>>>>>> +        location_t loc = gimple_location (gsi_stmt (gsi_last_bb 
>>>>>> (conds[0])));
>>>>>> +        warning_at (loc, OPT_Wcoverage_too_many_conditions,
>>>>>> +            "Too many conditions (found %u); giving up coverage",
>>>>>> +            conds.length ());
>>>>>> +        continue;
>>>>>> +    }
>>>>>> +    conds.sort (topological_cmp, &ctx.top_index);
>>>>>> +    vec<basic_block>& subgraph = paths_between (ctx, fnblocks, 
>>>>>> conds);
>>>>>> +    subgraph.sort (topological_cmp, &ctx.top_index);
>>>>>> +    const unsigned index = cov->m_index.length () - 1;
>>>>>> +    sbitmap condm = cov->m_maps[0 + 2*index];
>>>>>> +    sbitmap subgm = cov->m_maps[1 + 2*index];
>>>>>> +    for (basic_block b : conds)
>>>>>> +        bitmap_set_bit (condm, b->index);
>>>>>> +    for (basic_block b : subgraph)
>>>>>> +        bitmap_set_bit (subgm, b->index);
>>>>>> +    cov->m_blocks.safe_splice (subgraph);
>>>>>> +    cov->m_index.safe_push (cov->m_blocks.length ());
>>>>>> +    }
>>>>>> +
>>>>>> +    if (!have_dom)
>>>>>> +    free_dominance_info (fn, CDI_DOMINATORS);
>>>>>> +    if (!have_post_dom)
>>>>>> +    free_dominance_info (fn, CDI_POST_DOMINATORS);
>>>>>> +
>>>>>> +    cov->m_masks.safe_grow_cleared (2 * cov->m_index.last ());
>>>>>> +    const size_t length = cov_length (cov);
>>>>>> +    for (size_t i = 0; i != length; i++)
>>>>>> +    masking_vectors (ctx, cov_blocks (cov, i), cov_maps (cov, i),
>>>>>> +             cov_masks (cov, i));
>>>>>> +
>>>>>> +    return cov;
>>>>>> +}
>>>>>> +
>>>>>> +namespace
>>>>>> +{
>>>>>> +
>>>>>> +/* Stores the incoming edge and previous counters (in SSA form) 
>>>>>> on that edge
>>>>>> +   for the node e->deston that edge for the node e->dest.  The 
>>>>>> counters record
>>>>>> +   the seen-true (0), seen-false (1), and current-mask (2).  They 
>>>>>> are stored in
>>>>>> +   an array rather than proper members for access-by-index as the 
>>>>>> code paths
>>>>>> +   tend to be identical for the different counters.  */
>>>>>> +struct counters
>>>>>> +{
>>>>>> +    edge e;
>>>>>> +    tree counter[3];
>>>>>> +    tree& operator [] (size_t i) { return counter[i]; }
>>>>>> +};
>>>>>> +
>>>>>> +/* Find the counters for the incoming edge, or null if the edge 
>>>>>> has not been
>>>>>> +   recorded (could be for complex incoming edges).  */
>>>>>> +counters*
>>>>>> +find_counters (vec<counters>& candidates, edge e)
>>>>>> +{
>>>>>> +    for (counters& candidate : candidates)
>>>>>> +    if (candidate.e == e)
>>>>>> +        return &candidate;
>>>>>> +    return NULL;
>>>>>> +}
>>>>>> +
>>>>>> +/* Resolve the SSA for a specific counter.  If it is not modified 
>>>>>> by any
>>>>>> +   incoming edges, simply forward it, otherwise create a phi node 
>>>>>> of all the
>>>>>> +   candidate counters and return it.  */
>>>>>> +tree
>>>>>> +resolve_counter (vec<counters>& cands, size_t kind)
>>>>>> +{
>>>>>> +    gcc_assert (!cands.is_empty ());
>>>>>> +    gcc_assert (kind < 3);
>>>>>> +
>>>>>> +    counters& fst = cands[0];
>>>>>> +
>>>>>> +    if (!fst.e || fst.e->dest->preds->length () == 1)
>>>>>> +    {
>>>>>> +    gcc_assert (cands.length () == 1);
>>>>>> +    return fst[kind];
>>>>>> +    }
>>>>>> +
>>>>>> +    tree zero0 = build_int_cst (gcov_type_node, 0);
>>>>>> +    tree ssa = make_ssa_name (gcov_type_node);
>>>>>> +    gphi *phi = create_phi_node (ssa, fst.e->dest);
>>>>>> +    for (edge e : fst.e->dest->preds)
>>>>>> +    {
>>>>>> +    counters *prev = find_counters (cands, e);
>>>>>> +    if (prev)
>>>>>> +        add_phi_arg (phi, (*prev)[kind], e, UNKNOWN_LOCATION);
>>>>>> +    else
>>>>>> +    {
>>>>>> +        tree zero = make_ssa_name (gcov_type_node);
>>>>>> +        gimple_stmt_iterator gsi = gsi_after_labels (e->src);
>>>>>> +        gassign *set = gimple_build_assign (zero, zero0);
>>>>>> +        gsi_insert_before (&gsi, set, GSI_NEW_STMT);
>>>>>> +        add_phi_arg (phi, zero, e, UNKNOWN_LOCATION);
>>>>>> +    }
>>>>>> +    }
>>>>>> +    return ssa;
>>>>>> +}
>>>>>> +
>>>>>> +/* Resolve all the counters for a node.  Note that the edge is 
>>>>>> undefined, as
>>>>>> +   the counters are intended to form the base to push to the 
>>>>>> successors, and
>>>>>> +   because the is only meaningful for nodes with a single 
>>>>>> predecessor.  */
>>>>>> +counters
>>>>>> +resolve_counters (vec<counters>& cands)
>>>>>> +{
>>>>>> +    counters next;
>>>>>> +    next[0] = resolve_counter (cands, 0);
>>>>>> +    next[1] = resolve_counter (cands, 1);
>>>>>> +    next[2] = resolve_counter (cands, 2);
>>>>>> +    return next;
>>>>>> +}
>>>>>> +
>>>>>> +}
>>>>>> +
>>>>>> +/* Add instrumentation to a decision subgraph.  expr should be the
>>>>>> +   (topologically sorted) block of nodes returned by cov_blocks, 
>>>>>> maps the
>>>>>> +   bitmaps returned by cov_maps, and masks the block of bitsets 
>>>>>> returned by
>>>>>> +   cov_masks.  condno should be the index of this condition in 
>>>>>> the function,
>>>>>> +   i.e. the same argument given to cov_{masks,graphs}.  expr may 
>>>>>> contain nodes
>>>>>> +   in-between the conditions, e.g.  when an operand contains a 
>>>>>> function call,
>>>>>> +   or there is a setjmp and the cfg is filled with complex edges.
>>>>>> +
>>>>>> +   Every node is annotated with three counters; the true, false, 
>>>>>> and mask
>>>>>> +   value.  First, walk the graph and determine what if there are 
>>>>>> multiple
>>>>>> +   possible values for either accumulator depending on the path 
>>>>>> taken, in which
>>>>>> +   case a phi node is created and registered as the accumulator. 
>>>>>> Then, those
>>>>>> +   values are pushed as accumulators to the immediate successors. 
>>>>>> For some
>>>>>> +   very particular programs there may be multiple paths into the 
>>>>>> expression
>>>>>> +   (e.g. when prior terms are determined by a surrounding 
>>>>>> conditional) in which
>>>>>> +   case the default zero-counter is pushed, otherwise all 
>>>>>> predecessors will
>>>>>> +   have been considered before the successor because of 
>>>>>> topologically ordered
>>>>>> +   traversal.  Finally, expr is traversed again to look for edges 
>>>>>> to the
>>>>>> +   outcomes, that is, edges with a destination outside of expr, 
>>>>>> and the local
>>>>>> +   accumulators are flushed to the global gcov counters on these 
>>>>>> edges.  In
>>>>>> +   some cases there are edge splits that cause 3+ edges to the 
>>>>>> two outcome
>>>>>> +   nodes.
>>>>>> +
>>>>>> +   If a complex edge is taken (e.g. on a longjmp) the 
>>>>>> accumulators are
>>>>>> +   attempted poisoned so that there would be no change to the 
>>>>>> global counters,
>>>>>> +   but this has proven unreliable in the presence of undefined 
>>>>>> behavior, see
>>>>>> +   the setjmp003 test.
>>>>>> +
>>>>>> +   It is important that the flushes happen on the basic condition 
>>>>>> outgoing
>>>>>> +   edge, otherwise flushes could be lost to exception handling or 
>>>>>> other
>>>>>> +   abnormal control flow.  */
>>>>>> +size_t
>>>>>> +instrument_decisions (array_slice<basic_block> expr, size_t condno,
>>>>>> +              array_slice<sbitmap> maps, array_slice<uint64_t> 
>>>>>> masks)
>>>>>> +{
>>>>>> +    tree zero = build_int_cst (gcov_type_node, 0);
>>>>>> +    tree poison = build_int_cst (gcov_type_node, ~0ULL);
>>>>>> +    const sbitmap core = maps[0];
>>>>>> +    const sbitmap allg = maps[1];
>>>>>> +
>>>>>> +    hash_map<basic_block, vec<counters>> table;
>>>>>> +    counters zerocounter;
>>>>>> +    zerocounter.e = NULL;
>>>>>> +    zerocounter[0] = zero;
>>>>>> +    zerocounter[1] = zero;
>>>>>> +    zerocounter[2] = zero;
>>>>>> +
>>>>>> +    unsigned xi = 0;
>>>>>> +    tree rhs = build_int_cst (gcov_type_node, 1ULL << xi);
>>>>>> +    for (basic_block current : expr)
>>>>>> +    {
>>>>>> +    vec<counters>& candidates = table.get_or_insert (current);
>>>>>> +    if (candidates.is_empty ())
>>>>>> +        candidates.safe_push (zerocounter);
>>>>>> +    counters prev = resolve_counters (candidates);
>>>>>> +
>>>>>> +    int increment = 0;
>>>>>> +    for (edge e : current->succs)
>>>>>> +    {
>>>>>> +        counters next = prev;
>>>>>> +        next.e = e;
>>>>>> +
>>>>>> +        if (bitmap_bit_p (core, e->src->index) && (e->flags & 
>>>>>> EDGE_CONDITION))
>>>>>> +        {
>>>>>> +        const int k = condition_index (e->flags);
>>>>>> +        next[k] = emit_bitwise_op (e, prev[k], BIT_IOR_EXPR, rhs);
>>>>>> +        if (masks[2*xi + k])
>>>>>> +        {
>>>>>> +            tree m = build_int_cst (gcov_type_node, masks[2*xi + 
>>>>>> k]);
>>>>>> +            next[2] = emit_bitwise_op (e, prev[2], BIT_IOR_EXPR, m);
>>>>>> +        }
>>>>>> +        increment = 1;
>>>>>> +        }
>>>>>> +        else if (e->flags & EDGE_COMPLEX)
>>>>>> +        {
>>>>>> +        /* A complex edge has been taken - wipe the accumulators and
>>>>>> +           poison the mask so that this path does not contribute to
>>>>>> +           coverage.  */
>>>>>> +        next[0] = poison;
>>>>>> +        next[1] = poison;
>>>>>> +        next[2] = poison;
>>>>>> +        }
>>>>>> +        table.get_or_insert (e->dest).safe_push (next);
>>>>>> +    }
>>>>>> +    xi += increment;
>>>>>> +    if (increment)
>>>>>> +        rhs = build_int_cst (gcov_type_node, 1ULL << xi);
>>>>>> +    }
>>>>>> +
>>>>>> +    gcc_assert (xi == bitmap_count_bits (core));
>>>>>> +
>>>>>> +    const tree relaxed = build_int_cst (integer_type_node, 
>>>>>> MEMMODEL_RELAXED);
>>>>>> +    const bool atomic = flag_profile_update == 
>>>>>> PROFILE_UPDATE_ATOMIC;
>>>>>> +    const tree atomic_ior = builtin_decl_explicit
>>>>>> +    (TYPE_PRECISION (gcov_type_node) > 32
>>>>>> +     ? BUILT_IN_ATOMIC_FETCH_OR_8
>>>>>> +     : BUILT_IN_ATOMIC_FETCH_OR_4);
>>>>>> +
>>>>>> +    /* Flush to the gcov accumulators.  */
>>>>>> +    for (const basic_block b : expr)
>>>>>> +    {
>>>>>> +    if (!bitmap_bit_p (core, b->index))
>>>>>> +        continue;
>>>>>> +
>>>>>> +    for (edge e : b->succs)
>>>>>> +    {
>>>>>> +        /* Flush the accumulators on leaving the Boolean 
>>>>>> function. The
>>>>>> +           destination may be inside the function only when it 
>>>>>> returns to
>>>>>> +           the loop header, such as do { ... } while (x);  */
>>>>>> +        if (bitmap_bit_p (allg, e->dest->index)) {
>>>>>> +        if (!(e->flags & EDGE_DFS_BACK))
>>>>>> +            continue;
>>>>>> +        if (e->dest != expr[0])
>>>>>> +            continue;
>>>>>> +        }
>>>>>> +
>>>>>> +        vec<counters> *cands = table.get (e->dest);
>>>>>> +        gcc_assert (cands);
>>>>>> +        counters *prevp = find_counters (*cands, e);
>>>>>> +        gcc_assert (prevp);
>>>>>> +        counters prev = *prevp;
>>>>>> +
>>>>>> +        /* _true &= ~mask, _false &= ~mask  */
>>>>>> +        counters next;
>>>>>> +        next[2] = emit_bitwise_op (e, prev[2], BIT_NOT_EXPR);
>>>>>> +        next[0] = emit_bitwise_op (e, prev[0], BIT_AND_EXPR, 
>>>>>> next[2]);
>>>>>> +        next[1] = emit_bitwise_op (e, prev[1], BIT_AND_EXPR, 
>>>>>> next[2]);
>>>>>> +
>>>>>> +        /* _global_true |= _true, _global_false |= _false  */
>>>>>> +        for (size_t k = 0; k != 2; ++k)
>>>>>> +        {
>>>>>> +        tree ref = tree_coverage_counter_ref (GCOV_COUNTER_CONDS,
>>>>>> +                              2*condno + k);
>>>>>> +        if (atomic)
>>>>>> +        {
>>>>>> +            ref = unshare_expr (ref);
>>>>>> +            gcall *flush = gimple_build_call (atomic_ior, 3,
>>>>>> +                              build_addr (ref),
>>>>>> +                              next[k], relaxed);
>>>>>> +            gsi_insert_on_edge (e, flush);
>>>>>> +        }
>>>>>> +        else
>>>>>> +        {
>>>>>> +            tree get = emit_assign (e, ref);
>>>>>> +            tree put = emit_bitwise_op (e, next[k], BIT_IOR_EXPR, 
>>>>>> get);
>>>>>> +            emit_assign (e, unshare_expr (ref), put);
>>>>>> +        }
>>>>>> +        }
>>>>>> +    }
>>>>>> +    }
>>>>>> +
>>>>>> +    return xi;
>>>>>> +}
>>>>>> +
>>>>>> +#undef CONDITIONS_MAX_TERMS
>>>>>> +#undef EDGE_CONDITION
>>>>>> +
>>>>>>   /* Do initialization work for the edge profiler.  */
>>>>>>   /* Add code:
>>>>>> @@ -837,7 +1886,7 @@ tree_profiling (void)
>>>>>>         thunk = true;
>>>>>>         /* When generate profile, expand thunk to gimple so it can be
>>>>>>            instrumented same way as other functions.  */
>>>>>> -      if (profile_arc_flag)
>>>>>> +      if (profile_arc_flag || condition_coverage_flag)
>>>>>>           expand_thunk (node, false, true);
>>>>>>         /* Read cgraph profile but keep function as thunk at 
>>>>>> profile-use
>>>>>>            time.  */
>>>>>> @@ -882,7 +1931,7 @@ tree_profiling (void)
>>>>>>     release_profile_file_filtering ();
>>>>>>     /* Drop pure/const flags from instrumented functions.  */
>>>>>> -  if (profile_arc_flag || flag_test_coverage)
>>>>>> +  if (profile_arc_flag || condition_coverage_flag || 
>>>>>> flag_test_coverage)
>>>>>>       FOR_EACH_DEFINED_FUNCTION (node)
>>>>>>         {
>>>>>>       if (!gimple_has_body_p (node->decl)
>>>>>> @@ -914,7 +1963,7 @@ tree_profiling (void)
>>>>>>         push_cfun (DECL_STRUCT_FUNCTION (node->decl));
>>>>>> -      if (profile_arc_flag || flag_test_coverage)
>>>>>> +      if (profile_arc_flag || condition_coverage_flag || 
>>>>>> flag_test_coverage)
>>>>>>       FOR_EACH_BB_FN (bb, cfun)
>>>>>>         {
>>>>>>           gimple_stmt_iterator gsi;
>>>>>> @@ -999,7 +2048,7 @@ pass_ipa_tree_profile::gate (function *)
>>>>>>        disabled.  */
>>>>>>     return (!in_lto_p && !flag_auto_profile
>>>>>>         && (flag_branch_probabilities || flag_test_coverage
>>>>>> -          || profile_arc_flag));
>>>>>> +          || profile_arc_flag || condition_coverage_flag));
>>>>>>   }
>>>>>>   } // anon namespace
>>>>>> diff --git a/gcc/tree.h b/gcc/tree.h
>>>>>> index 086b55f0375..f4186a72b5c 100644
>>>>>> --- a/gcc/tree.h
>>>>>> +++ b/gcc/tree.h
>>>>>> @@ -1358,6 +1358,10 @@ class auto_suppress_location_wrappers
>>>>>>     ~auto_suppress_location_wrappers () { 
>>>>>> --suppress_location_wrappers; }
>>>>>>   };
>>>>>> +/* COND_EXPR identificer/discriminator accessors.  */
>>>>>> +#define SET_EXPR_UID(t, v) EXPR_CHECK ((t))->exp.condition_uid = (v)
>>>>>> +#define EXPR_COND_UID(t) EXPR_CHECK ((t))->exp.condition_uid
>>>>>> +
>>>>>>   /* In a TARGET_EXPR node.  */
>>>>>>   #define TARGET_EXPR_SLOT(NODE) TREE_OPERAND_CHECK_CODE (NODE, 
>>>>>> TARGET_EXPR, 0)
>>>>>>   #define TARGET_EXPR_INITIAL(NODE) TREE_OPERAND_CHECK_CODE (NODE, 
>>>>>> TARGET_EXPR, 1)
>>>>>> diff --git a/libgcc/libgcov-merge.c b/libgcc/libgcov-merge.c
>>>>>> index 5d6e17d1483..eed3556373b 100644
>>>>>> --- a/libgcc/libgcov-merge.c
>>>>>> +++ b/libgcc/libgcov-merge.c
>>>>>> @@ -33,6 +33,11 @@ void __gcov_merge_add (gcov_type *counters 
>>>>>> __attribute__ ((unused)),
>>>>>>                          unsigned n_counters __attribute__ 
>>>>>> ((unused))) {}
>>>>>>   #endif
>>>>>> +#ifdef L_gcov_merge_ior
>>>>>> +void __gcov_merge_ior (gcov_type *counters  __attribute__ 
>>>>>> ((unused)),
>>>>>> +               unsigned n_counters __attribute__ ((unused))) {}
>>>>>> +#endif
>>>>>> +
>>>>>>   #ifdef L_gcov_merge_topn
>>>>>>   void __gcov_merge_topn (gcov_type *counters  __attribute__ 
>>>>>> ((unused)),
>>>>>>               unsigned n_counters __attribute__ ((unused))) {}
>>>>>
>>>>
>>>
>>
> 


  reply	other threads:[~2024-02-21 19:09 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-12-31 15:51 Jørgen Kvalsvik
2023-12-31 15:51 ` [PATCH v9 2/2] Add gcov MC/DC tests for GDC Jørgen Kvalsvik
2023-12-31 22:06   ` Iain Buclaw
2024-02-22 13:27   ` Jan Hubicka
2024-01-02 21:07 ` [PATCH v9 1/2] Add condition coverage (MC/DC) Jørgen Kvalsvik
2024-01-09  9:04   ` Ping: " Jørgen Kvalsvik
2024-01-22 10:21     ` Ping^2 " Jørgen Kvalsvik
2024-01-29  8:53       ` Ping^3 " Jørgen Kvalsvik
2024-02-06 13:09         ` Ping^4 " Jørgen Kvalsvik
2024-02-21 19:08           ` Jørgen Kvalsvik [this message]
2024-01-29 23:31     ` Ping: " Fangrui Song
2024-01-30  9:33       ` Jørgen Kvalsvik
2024-02-22 13:26 ` Jan Hubicka
2024-02-22 14:24   ` Jørgen Kvalsvik

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=31fa5f1a-236e-4b40-a2e4-3fe5c36a1b3d@lambda.is \
    --to=j@lambda.is \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=hubicka@ucw.cz \
    --cc=jh@suse.cz \
    --cc=richard.guenther@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).