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: Re: Ping^4 [PATCH v9 1/2] Add condition coverage (MC/DC)
Date: Tue, 6 Feb 2024 14:09:22 +0100 [thread overview]
Message-ID: <15dce852-1419-49bd-89f4-dc6cfffab49f@lambda.is> (raw)
In-Reply-To: <289d466e-50f2-49af-9e40-889c12a763dc@lambda.is>
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))) {}
>>>>
>>>
>>
>
next prev parent reply other threads:[~2024-02-06 13: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 ` Jørgen Kvalsvik [this message]
2024-02-21 19:08 ` Ping^5 " Jørgen Kvalsvik
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=15dce852-1419-49bd-89f4-dc6cfffab49f@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).