[Ping^3][PATCH v2] Add attribute and pragma for coverage suppression
Jørgen Kvalsvik <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
On 7/20/26 18:26, Jørgen Kvalsvik wrote:
> On 7/8/26 15:08, Jørgen Kvalsvik wrote:
>> On 6/26/26 14:22, Jørgen Kvalsvik wrote:
>>> Was "Add function attr+pragma for coverage suppression".
>>>
>>> I've added a test for the pragma itself and tweaked the messages a bit.
>>> I also replaced the push/pop with begin/end (inspired by openmp) since
>>> it really is a block-type and not a stack. The forward declarations for
>>> suppress_coverage_begin/end were moved from profile.h to c-pragma.h
>>> where they really belong.
>>>
>>> --
>>>
>>> Add support for suppressing coverage, either at the function level
>>> with the attribute [[gnu::suppress_coverage]] or at the line level
>>> with #pragma GCC suppress_coverage begin/end. This is quite useful
>>> for things like unreachable states which would score worse on coverage
>>> despite being better code, or when a specific functions should be
>>> omitted from coverage.
>>>
>>> A motivating example from the wild are contracts in debug
>>> builds. Let's assume we have a precondition assert macro:
>>>
>>> #define REQUIRE(pred) assert (((void)"Precondition violated",
>>> pred))
>>>
>>> int div (int x, int y)
>>> {
>>> REQUIRE (y != 0);
>>> return x / y;
>>> }
>>>
>>> The REQUIRE/assert introduces a branch in code that is otherwise a
>>> straight line, and if the function is never called with y = 0 (which
>>> is undefined), the assert-failure branch will not be covered, which
>>> pollutes the coverage report. GCC and gcov can already filter out on
>>> the function and file level (e.g. -fprofile-filter-files=<regex>), but
>>> that is not sufficient for this use case. The suppress_coverage
>>> attribute is a high resolution tool for very specific bits of code so
>>> that shouldn't be counted towards coverage.
>>>
>>> We can change the REQUIRE macro to this to make the problem
>>> go away:
>>>
>>> #define REQUIRE(pred) do { \
>>> _Pragma ("GCC suppress_coverage begin"); \
>>> assert (((void)"Precondition violated", pred)); \
>>> _Pragma ("GCC suppress_coverage end"); \
>>> } while (0)
>>>
>>> --
>>>
>>> DEMO
>>>
>>> gcc div.c -o div --coverage && gcov -fbc div
>>>
>>> Without coverage suppression. The fallthrough branch is taken 0 times,
>>> and we have 50% branch coverage.
>>>
>>> Function 'div'
>>> Lines executed:100.00% of 3
>>> Branches executed:100.00% of 2
>>> Taken at least once:50.00% of 2
>>> No calls
>>> -: 0:Source:div.c
>>> -: 0:Graph:div.gcno
>>> -: 0:Data:div.gcda
>>> -: 0:Runs:1
>>> -: 1:#include <assert.h>
>>> -: 2:
>>> -: 3:#define REQUIRE(pred, msg) \
>>> -: 4: assert (((void)msg, pred))
>>> -: 5:
>>> function div called 1 returned 100% blocks executed 75%
>>> 1: 6:double div (double x, double y)
>>> -: 7:{
>>> -: 8: /* gcov reports that this function has no
>>> branches. */
>>> 1*: 9: REQUIRE (y != 0, "division by zero");
>>> branch 0 taken 0 (fallthrough)
>>> branch 1 taken 1
>>> call 2 never executed
>>> 1: 10: return x / y;
>>> -: 11:}
>>> -: 12:
>>> function main called 1 returned 100% blocks executed 100%
>>> 1: 13:int main() {
>>> 1: 14: return div(0, 5);
>>> call 0 returned 1
>>> -: 15:}
>>>
>>> The branch is effectively gone with suppresion:
>>>
>>> Function 'div'
>>> Lines executed:100.00% of 2 (1 of 3 suppressed)
>>> Branches executed:0.00% of 0 (2 of 2 suppressed)
>>> Taken at least once:0.00% of 0
>>> Calls executed:0.00% of 0 (1 of 1 suppressed)
>>> -: 0:Source:div.c
>>> -: 0:Graph:div.gcno
>>> -: 0:Data:div.gcda
>>> -: 0:Runs:1
>>> -: 1:#include <assert.h>
>>> -: 2:
>>> -: 3:#define REQUIRE(pred, msg) do { \
>>> -: 4: _Pragma ("GCC suppress_coverage
>>> begin"); \
>>> -: 5: assert (((void)msg, pred)); \
>>> -: 6: _Pragma ("GCC suppress_coverage
>>> end"); \
>>> -: 7:} while (0)
>>> -: 8:
>>> function div called 1 returned 100% blocks executed 80%
>>> 1: 9:double div (double x, double y)
>>> -: 10:{
>>> -: 11: /* gcov reports that this function has no
>>> branches. */
>>> #: 12: REQUIRE (y != 0, "division by zero");
>>> 1: 13: return x / y;
>>> -: 14:}
>>> -: 15:
>>> function main called 1 returned 100% blocks executed 100%
>>> 1: 16:int main() {
>>> 1: 17: return div(0, 5);
>>> call 0 returned 1
>>> -: 18:}
>>>
>>> --
>>>
>>> IMPLEMENTATION DETAILS
>>>
>>> We record the expanded source location of the pragma and consult them
>>> during tree-cfg construction. If a statement's location is anchored
>>> between a begin/end, we record the basic block suppressed in the new
>>> SUPPRESS record. We split a block when only parts of it have coverage
>>> suppressed, and take suppression into account when we consider merging
>>> blocks, generally prohibiting merges with different suppression flags.
>>>
>>> This feature is designed so that it does not actually disable the arc
>>> profiling instrumentation itself as we don't want this attribute
>>> messing with profile-guided optimization or other uses. Instead, we
>>> do the actual suppression in gcov. However, when the whole function
>>> is within a begin/end, or when annotated with
>>> [[gnu::suppress_coverage]], we can disable MC/DC and prime path
>>> coverage as those are only used for coverage, not pgo. This just
>>> works; we just omit the record, and gcov is none the wiser.
>>>
>>> --
>>>
>>> GCOV
>>>
>>> The coverage instrumentation is not actually changed in any meaningful
>>> way, suppressing coverage is about interpretation. Rather than changing
>>> the instrumentation, we add a new SUPPRESS record with metadata which
>>> tells gcov which blocks should be blacklisted from the coverage
>>> report. When gcov prints its report it checks blocks against the
>>> SUPPRESS record, and by extension arc, condition, and path, against the
>>> blacklist and decides whether or not to include it.
>>>
>>> Prime paths turned out to be tricky. If we suppress out a subsequence we
>>> want to unify the paths that used to differ but now don't care about
>>> the path through the suppressed subgraph, and either may have been
>>> covered.
>>>
>>> Function-level suppressions are implemented by GCC recording the ENTRY
>>> block (0) as suppressed. This block cannot be marked by user programs
>>> anyway so we get to claim it, and it makes sense that an ignored entry
>>> block implies the whole function.
>>>
>>> --
>>>
>>> gcc/c-family/ChangeLog:
>>>
>>> * c-attribs.cc (handle_suppress_coverage_attribute): New
>>> function.
>>> * c-pragma.cc (handle_pragma_suppress_coverage): New function.
>>> (init_pragma): Register pragma GCC suppress_coverage.
>>> * c-pragma.h (suppress_coverage_begin): New forward declaration.
>>> (suppress_coverage_end): Ditto.
>>>
>>> gcc/ChangeLog:
>>>
>>> * cfg-flags.def (COVERAGE_SUPPRESSED): New flag.
>>> * doc/extend.texi: Add sections on suppress_coverage.
>>> * doc/gcov.texi: Add sections on suppress_coverage.
>>> * gcov-dump.cc (tag_suppress): New function.
>>> * gcov-io.h (GCOV_TAG_SUPPRESS): New.
>>> (GCOV_TAG_SUPPRESS_LENGTH): New.
>>> (GCOV_TAG_SUPPRESS_NUM): New.
>>> * gcov.cc (struct arc_info): Add field suppressed.
>>> (block_info::block_info): Initialize suppressed.
>>> (line_info::line_info): Initialize suppressed.
>>> (struct coverage_info): Add fields lines_suppressed,
>>> (struct path_info): Add fields residual_paths,
>>> residual_covered.
>>> (executed_summary): Add suppressed argument.
>>> (tombstone_p): New function.
>>> (remove_tombstones): New function.
>>> (tombstone_subsequence_p): New function.
>>> (subsumed_by_any_p): New function.
>>> (path_info::suppress_blocks): New function.
>>> (path_info::get_paths): New function.
>>> (path_info::count): New function.
>>> (path_info::suppressed_count): New function.
>>> (path_info::suppressed_p): New function.
>>> (find_arc): Return NULL if arc cannot be found.
>>> (main): Pass total_suppressed to executed_summary.
>>> (output_intermediate_json_line): Skip suppressed.
>>> (json_set_prime_path_coverage): Include suppressed counts.
>>> (output_json_intermediate_file): Likewise.
>>> (process_all_functions): Record block, arc suppressed status.
>>> (generate_results): Count total suppressed.
>>> (read_graph_file): Read SUPPRESS tag.
>>> (add_branch_counts): Count suppressed.
>>> (add_condition_counts): Likewise.
>>> (add_path_counts): Likewise.
>>> (function_summary): Print info on suppressed blocks.
>>> (file_summary): Count suppressed.
>>> (add_line_counts): Likewise.
>>> (accumulate_line_info): Likewise.
>>> (accumulate_line_counts): Likewise.
>>> (output_conditions): Skip suppressed.
>>> (output_branch_count): Likewise.
>>> (print_prime_path_lines): Annotate suppressed arcs.
>>> (print_prime_path_source): Likewise, and widen the annotation
>>> column.
>>> (output_path_coverage): Print info on suppressed paths.
>>> (output_line_beginning): Prefix suppressed lines with '#'.
>>> (output_line_details): Pass suppressed.
>>> (output_lines): Likewise.
>>> * profile.cc (suppress_coverage): New function.
>>> (suppress_coverage_unset): New function.
>>> (coverage_suppressed_p): New function.
>>> (any_block_coverage_suppressed_p): New function.
>>> (suppress_coverage_begin): New function.
>>> (suppress_coverage_end): New function.
>>> (in_pragma_suppress_coverage_p): New function.
>>> (location_in_pragma_suppress_coverage_p): New function.
>>> (fn_in_pragma_suppress_coverage_p): New function.
>>> (branch_prob): Write SUPPRESS record, skip MC/DC and path
>>> coverage when the entire function is suppressed.
>>> * profile.h (in_pragma_suppress_coverage_p): New forward
>>> declaration.
>>> (location_in_pragma_suppress_coverage_p): Ditto.
>>> (coverage_suppressd_p): Ditto.
>>> (suppress_coverage): Ditto.
>>> (suppress_coverage_unset): Ditto.
>>> * tree-cfg.cc (gimple_empty_block_p): New forward declaration.
>>> (make_blocks_1): Set coverage-suppresesed on blocks.
>>> (make_blocks): Sometimes insert NOP to help coverage
>>> suppression on blocks.
>>> (gimple_can_merge_blocks_p): Don't merge coverage-suppressed
>>> block with non-suppressed blocks.
>>> (gimple_merge_blocks): Likewise.
>>> (gimple_split_edge): Propagate coverage-suppressed on block
>>> splits.
>>> (gimple_split_block): Likewise.
>>> (gimple_duplicate_bb): Likewise.
>>> (insert_cond_bb): Likewise.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> * g++.dg/gcov/gcov-24.C: New test.
>>> * g++.dg/gcov/gcov-25.C: New test.
>>> * g++.dg/gcov/gcov-26.C: New test.
>>> * g++.dg/gcov/gcov-27.C: New test.
>>> * gcc.dg/pragma-suppress-coverage.c: New test.
>>> * gcc.misc-tests/gcov-35.c: New test.
>>> * gcc.misc-tests/gcov-36.c: New test.
>>> * gcc.misc-tests/gcov-37.c: New test.
>>> * gcc.misc-tests/gcov-38.c: New test.
>>> * gcc.misc-tests/gcov-39.c: New test.
>>> * gcc.misc-tests/gcov-40.c: New test.
>>> * gcc.misc-tests/gcov-40.h: New test.
>>> ---
>>> gcc/c-family/c-attribs.cc | 21 +
>>> gcc/c-family/c-pragma.cc | 39 +
>>> gcc/c-family/c-pragma.h | 4 +
>>> gcc/cfg-flags.def | 3 +
>>> gcc/doc/extend.texi | 98 ++
>>> gcc/doc/gcov.texi | 82 +-
>>> gcc/gcov-dump.cc | 22 +
>>> gcc/gcov-io.h | 3 +
>>> gcc/gcov.cc | 532 ++++++++-
>>> gcc/profile.cc | 157 ++-
>>> gcc/profile.h | 7 +
>>> gcc/testsuite/g++.dg/gcov/gcov-24.C | 1022 ++++++++++++++++
>>> gcc/testsuite/g++.dg/gcov/gcov-25.C | 23 +
>>> gcc/testsuite/g++.dg/gcov/gcov-26.C | 548 +++++++++
>>> gcc/testsuite/g++.dg/gcov/gcov-27.C | 80 ++
>>> .../gcc.dg/pragma-suppress-coverage.c | 32 +
>>> gcc/testsuite/gcc.misc-tests/gcov-35.c | 1061 +++++++++++++++++
>>> gcc/testsuite/gcc.misc-tests/gcov-36.c | 23 +
>>> gcc/testsuite/gcc.misc-tests/gcov-37.c | 243 ++++
>>> gcc/testsuite/gcc.misc-tests/gcov-38.c | 100 ++
>>> gcc/testsuite/gcc.misc-tests/gcov-39.c | 35 +
>>> gcc/testsuite/gcc.misc-tests/gcov-40.c | 43 +
>>> gcc/testsuite/gcc.misc-tests/gcov-40.h | 4 +
>>> gcc/tree-cfg.cc | 82 ++
>>> 24 files changed, 4194 insertions(+), 70 deletions(-)
>>> create mode 100644 gcc/testsuite/g++.dg/gcov/gcov-24.C
>>> create mode 100644 gcc/testsuite/g++.dg/gcov/gcov-25.C
>>> create mode 100644 gcc/testsuite/g++.dg/gcov/gcov-26.C
>>> create mode 100644 gcc/testsuite/g++.dg/gcov/gcov-27.C
>>> create mode 100644 gcc/testsuite/gcc.dg/pragma-suppress-coverage.c
>>> create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-35.c
>>> create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-36.c
>>> create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-37.c
>>> create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-38.c
>>> create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-39.c
>>> create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-40.c
>>> create mode 100644 gcc/testsuite/gcc.misc-tests/gcov-40.h
>>>
>>> diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
>>> index 88293c37e4ee..834d91343eed 100644
>>> --- a/gcc/c-family/c-attribs.cc
>>> +++ b/gcc/c-family/c-attribs.cc
>>> @@ -193,6 +193,9 @@ static tree
>>> handle_null_terminated_string_arg_attribute (tree *, tree, tree, int
>>> static tree handle_btf_decl_tag_attribute (tree *, tree, tree, int,
>>> bool *);
>>> static tree handle_btf_type_tag_attribute (tree *, tree, tree, int,
>>> bool *);
>>> +static tree handle_suppress_coverage_attribute (tree *, tree, tree,
>>> int,
>>> + bool *);
>>> +
>>> /* Helper to define attribute exclusions. */
>>> #define ATTR_EXCL(name, function, type, variable) \
>>> { name, function, type, variable }
>>> @@ -643,6 +646,8 @@ const struct attribute_spec
>>> c_common_gnu_attributes[] =
>>> handle_special_var_sec_attribute,
>>> attr_section_exclusions },
>>> { "access", 1, 3, false, true, true, false,
>>> handle_access_attribute, NULL },
>>> + { "suppress_coverage", 0, 0, true, false, false, false,
>>> + handle_suppress_coverage_attribute, NULL },
>>> /* Attributes used by Objective-C. */
>>> { "NSObject", 0, 0, true, false, false, false,
>>> handle_nsobject_attribute, NULL },
>>> @@ -5340,6 +5345,22 @@ handle_nonstring_attribute (tree *node, tree
>>> name, tree ARG_UNUSED (args),
>>> return NULL_TREE;
>>> }
>>> +/* Handle the "disable_coverge" attribute. We don't really do
>>> anything here,
>>> + just check later if it is set. */
>>> +static tree
>>> +handle_suppress_coverage_attribute (tree *node, tree name,
>>> + tree ARG_UNUSED (args),
>>> + int ARG_UNUSED (flags), bool *no_add_attrs)
>>> +{
>>> + if (TREE_CODE (*node) != FUNCTION_DECL)
>>> + {
>>> + warning (OPT_Wattributes, "%qE attribute ignored", name);
>>> + *no_add_attrs = true;
>>> + }
>>> +
>>> + return NULL_TREE;
>>> +}
>>> +
>>> /* Given a function type FUNCTYPE, returns the type of the parameter
>>> ARGNO or null if ARGNO exceeds the number of parameters. On
>>> failure
>>> set *NARGS to the number of function parameters. */
>>> diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc
>>> index 4903dcaa0823..d0d4ff69fd7c 100644
>>> --- a/gcc/c-family/c-pragma.cc
>>> +++ b/gcc/c-family/c-pragma.cc
>>> @@ -1041,6 +1041,43 @@ handle_pragma_diagnostic_early_pp (cpp_reader *)
>>> handle_pragma_diagnostic_impl<true, true> ();
>>> }
>>> +/* Parse #pragma GCC suppress_coverage begin|end to stop lines from
>>> contributing
>>> + towards (missing) coverage. */
>>> +static void
>>> +handle_pragma_suppress_coverage (cpp_reader*)
>>> +{
>>> + tree x;
>>> + location_t loc;
>>> + auto token = pragma_lex (&x);
>>> + enum { bad, begin, end } action = bad;
>>> +
>>> + if (token == CPP_NAME)
>>> + {
>>> + const char *op = IDENTIFIER_POINTER (x);
>>> + if (!strcmp (op, "begin"))
>>> + action = begin;
>>> + else if (!strcmp (op, "end"))
>>> + action = end;
>>> + }
>>> +
>>> + if (bad == action)
>>> + GCC_BAD ("%<#pragma GCC suppress_coverage%> must be followed by
>>> %<begin%> "
>>> + "or %<end%>");
>>> + else if (end == action)
>>> + {
>>> + if (!suppress_coverage_end (input_location))
>>> + GCC_BAD ("no matching begin for %<#pragma GCC suppress_coverage
>>> end%>");
>>> + }
>>> + else
>>> + {
>>> + if (!suppress_coverage_begin (input_location))
>>> + GCC_BAD ("%<#pragma GCC suppress_coverage begin%> "
>>> + "was already in effect, ignored");
>>> + }
>>> + if (pragma_lex (&x, &loc) != CPP_EOF)
>>> + GCC_BAD_AT (loc, "junk at end of %<#pragma GCC
>>> suppress_coverage%>");
>>> +}
>>> +
>>> /* Parse #pragma GCC target (xxx) to set target specific options. */
>>> static void
>>> handle_pragma_target(cpp_reader *)
>>> @@ -1837,6 +1874,8 @@ init_pragma (void)
>>> c_register_pragma (0, "weak", handle_pragma_weak);
>>> c_register_pragma ("GCC", "visibility", handle_pragma_visibility);
>>> + c_register_pragma ("GCC", "suppress_coverage",
>>> + handle_pragma_suppress_coverage);
>>> if (flag_preprocess_only)
>>> c_register_pragma_with_early_handler ("GCC", "diagnostic",
>>> diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
>>> index ff299f4e1a78..5d28cb45c669 100644
>>> --- a/gcc/c-family/c-pragma.h
>>> +++ b/gcc/c-family/c-pragma.h
>>> @@ -303,4 +303,8 @@ extern void c_pp_lookup_pragma (unsigned int,
>>> const char **, const char **);
>>> extern GTY(()) tree pragma_extern_prefix;
>>> +/* For recording #pragma GCC suppress_coverage locations. */
>>> +extern bool suppress_coverage_begin (location_t);
>>> +extern bool suppress_coverage_end (location_t);
>>> +
>>> #endif /* GCC_C_PRAGMA_H */
>>> diff --git a/gcc/cfg-flags.def b/gcc/cfg-flags.def
>>> index cbb4bb970272..0b75cf328b4b 100644
>>> --- a/gcc/cfg-flags.def
>>> +++ b/gcc/cfg-flags.def
>>> @@ -93,6 +93,9 @@ DEF_BASIC_BLOCK_FLAG(VISITED, 13)
>>> demand, and is available after calling
>>> compute_transaction_bits(). */
>>> DEF_BASIC_BLOCK_FLAG(IN_TRANSACTION, 14)
>>> +/* Set on blocks that are in #pragma GCC suppress_coverage blocks. */
>>> +DEF_BASIC_BLOCK_FLAG(COVERAGE_SUPPRESSED, 15)
>>> +
>>> #endif
>>> #ifdef DEF_EDGE_FLAG
>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>> index 7bd84e6d8cc3..412d463f81e0 100644
>>> --- a/gcc/doc/extend.texi
>>> +++ b/gcc/doc/extend.texi
>>> @@ -2770,6 +2770,19 @@ positional initialization will result in
>>> future breakage.
>>> GCC emits warnings based on this attribute by default; use
>>> @option{-Wno-designated-init} to suppress them.
>>> +@cindex @code{suppress_coverage}
>>> +@item suppress_coverage
>>> +This attribute applies to functions.
>>> +
>>> +The @code{suppress_coverage} attribute results in GCC not
>>> considering the
>>> +function or its lines, arcs, conditions, or paths towards the total
>>> +count. This attribute does nothing unless coverage is enabled,
>>> +e.g. with @option{--coverage} or @option{-fpath-coverage}. This is
>>> useful
>>> +to suppress coverage for functions that are uninteresting or
>>> impractical to
>>> +cover. If coverage is suppressed with this attribute then GCC will not
>>> +instrument for MC/DC @option{-fcondition-coverage} or prime path
>>> +coverage @option{-fpath-coverage}.
>>> +
>>> @atindex @code{error}
>>> @atindex @code{warning}
>>> @cindex functions that are supposed to be optimized away
>>> @@ -10092,6 +10105,7 @@ option is specified. @xref{OpenMP}, and
>>> @ref{OpenACC}.
>>> * Structure-Layout Pragmas::
>>> * Weak Pragmas::
>>> * Diagnostic Pragmas::
>>> +* Coverage Pragmas::
>>> * Visibility Pragmas::
>>> * Push/Pop Macro Pragmas::
>>> * Function Specific Option Pragmas::
>>> @@ -10556,6 +10570,90 @@ an error as well.
>>> @end table
>>> +@node Coverage Pragmas
>>> +@subsection Coverage Pragmas
>>> +
>>> +@table @code
>>> +@cindex pragma, suppress_coverage
>>> +@item #pragma GCC suppress_coverage begin
>>> +@itemx #pragma GCC suppress_coverage end
>>> +The lines of code between the @code{begin} and @code{end} will not be
>>> +considered towards any coverage. This is independent of the code
>>> +structure, and @code{end} may appear in a different scope than the
>>> +corresponding @code{begin}.
>>> +
>>> +This pragma is useful to suppress coverage for code that is
>>> unreachable yet
>>> +counts towards coverage and pollutes the report, which is a common
>>> +problem with defensive code or when asserting invariants and
>>> properties.
>>> +This attribute does nothing unless coverage is enabled, e.g. with
>>> +@option{--coverage} or @option{-fpath-coverage}.
>>> +
>>> +A @code{end} without a @code{begin}, or a @code{begin} when another is
>>> +already in effect, will generate a warning. A @code{begin} without a
>>> +matching @code{end} will suppress the rest of the file.
>>> +
>>> +@smallexample
>>> + #pragma GCC suppress_coverage begin
>>> + if (cond)
>>> + @{
>>> + /* Coverage is disbled for all statements in this block. */
>>> + foo ();
>>> + @}
>>> + else
>>> + @{
>>> + #pragma GCC suppress_coverage end
>>> + /* Coverage is restored. */
>>> + bar ();
>>> + @}
>>> +
>>> + /* Counts towards coverage. */
>>> + int a = 42;
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + @{
>>> + /* Does not count towards coverage. */
>>> + bar (&a);
>>> + ...
>>> + @}
>>> +@end smallexample
>>> +
>>> +Asserts introduce a branch, and the goal of asserting invariants is
>>> that
>>> +the assert should not trigger, which would leave the assertion-failure
>>> +branch uncovered. These branches should not be taken (or cannot, in
>>> +some cases), and would make full coverage impossible, yet clearly
>>> +contribute to correctness. By suppressing coverage for asserts we can
>>> +fix this problem.
>>> +
>>> +@smallexample
>>> +#define REQUIRE(pred, msg) do @{ \
>>> + _Pragma ("GCC suppress_coverage begin") \
>>> + assert (((void)msg, pred)); \
>>> + _Pragma ("GCC suppress_coverage end") \
>>> + @} while (0)
>>> +
>>> +double div (double x, double y)
>>> +@{
>>> + /* gcov reports that this function has no branches. */
>>> + REQUIRE (y != 0, "division by zero");
>>> + return x / y;
>>> +@}
>>> +@end smallexample
>>> +
>>> +@samp{pragma GCC suppress_coverage} can suppress expressions like the
>>> +condition in @code{if} statements:
>>> +
>>> +@smallexample
>>> +#pragma GCC suppress_coverage begin
>>> +if (x < y) // Does not count towards coverage.
>>> +#pragma GCC suppress_coverage end
>>> + @{
>>> + /* Counts towards coverage. */
>>> + foo ();
>>> + @}
>>> +@end smallexample
>>> +
>>> +@end table
>>> +
>>> @node Visibility Pragmas
>>> @subsection Visibility Pragmas
>>> diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
>>> index a5fed102026a..64ce9e3a152c 100644
>>> --- a/gcc/doc/gcov.texi
>>> +++ b/gcc/doc/gcov.texi
>>> @@ -228,7 +228,8 @@ path 4 not covered: lines 8 8(false) 11(true) 11
>>> 13(false) 16 17
>>> This means to cover path 2 you must run lines 8, 11, 13, 14, and 17,
>>> evaluating the decision at 8 false and the decisions at 11 and 13 to
>>> -@code{false}.
>>> +@code{false}. @code{<line>(suppress)} means that @code{<line>} is the
>>> +last line before a sequence of suppressed statements.
>>> @item --prime-paths-source [=@var{type}]
>>> Write path coverage to the output file, and write path summary info to
>>> @@ -671,7 +672,8 @@ containing no code. Unexecuted lines are marked
>>> @samp{#####} or
>>> non-exceptional paths or only exceptional paths such as C++ exception
>>> handlers, respectively. Given the @samp{-a} option, unexecuted
>>> blocks are
>>> marked @samp{$$$$$} or @samp{%%%%%}, depending on whether a basic
>>> block
>>> -is reachable via non-exceptional or exceptional paths.
>>> +is reachable via non-exceptional or exceptional paths. Suppressed
>>> lines
>>> +are marked with @samp{#}.
>>> Executed basic blocks having a statement with zero
>>> @var{execution_count}
>>> end with @samp{*} character and are colored with magenta color with
>>> the @option{-k} option. This functionality is not supported in Ada.
>>> @@ -1177,6 +1179,82 @@ decisions like @option{--prime-paths-source}
>>> but printed on a single
>>> line. This mode provides a good overview over the paths and for
>>> tracking how different tests and inputs exercises the code.
>>> +Coverage can be suppressed by using
>>> +@code{#pragma GCC suppress_coverage}. The code within @code{begin} and
>>> +@code{end} will not contribute to coverage totals. This is very useful
>>> +for unreachable or hard-to-reach code, precondition asserts/contracts,
>>> +or other code that is noise in the coverage report. Suppressed lines
>>> +are annotated with @samp{#} in the @var{execution_count} column.
>>> +
>>> +@smallexample
>>> +$ gcov -t tmp
>>> + -: 0:Source:tmp.c
>>> + -: 0:Graph:tmp.gcno
>>> + -: 0:Data:tmp.gcda
>>> + -: 0:Runs:1
>>> + -: 1:#include <stdio.h>
>>> + -: 2:#include <stdlib.h>
>>> + -: 3:
>>> + -: 4:int
>>> + 1: 5:main (void)
>>> + -: 6:@{
>>> + -: 7: int i, total;
>>> + 1: 8: total = 0;
>>> + -: 9:
>>> + 11: 10: for (i = 0; i < 10; i++)
>>> + 10: 11: total += i;
>>> + -: 12:
>>> + -: 13:#pragma GCC suppress_coverage begin
>>> + #: 14: if (i < 0)
>>> + #: 15: abort ();
>>> + -: 16:#pragma GCC suppress_coverage end
>>> + -: 17:
>>> + 1*: 18: int v = total > 100 ? 1 : 2;
>>> + -: 19:
>>> + 1*: 20: if (total != 45 && v == 1)
>>> + #####: 21: printf ("Failure\n");
>>> + -: 22: else
>>> + 1: 23: printf ("Success\n");
>>> + 1: 24: return 0;
>>> + -: 25:@}
>>> +@end smallexample
>>> +
>>> +New prime paths are computed when code is suppressed by merging the
>>> +suppressed code into a superblocks, and considering any path through
>>> the
>>> +superblock as coverage. This is an example where a full if-then-
>>> else is
>>> +suppressed. Path 4 is @code{total > 100} is followed by any path
>>> +through the @samp{(suppressed)} segment to the @code{return 0}.
>>> +
>>> +@smallexample
>>> +$ gcov -t --prime-paths-source tmp
>>> +path 4 not covered:
>>> +BB 3: 10: for (i = 0; i < 10; i++)
>>> +BB 3: 11: total += i;
>>> +BB 4: (false) 10: for (i = 0; i < 10; i++)
>>> +BB 5: (true) 13: int v = total > 100 ? 1 : 2;
>>> +BB 6: (suppress) 13: int v = total > 100 ? 1 : 2;
>>> +BB 12: 21: return 0;
>>> +
>>> + 1: 5:main (void)
>>> + -: 6:@{
>>> + -: 7: int i, total;
>>> + 1: 8: total = 0;
>>> + -: 9:
>>> + 11: 10: for (i = 0; i < 10; i++)
>>> + 10: 11: total += i;
>>> + -: 12:
>>> + 1*: 13: int v = total > 100 ? 1 : 2;
>>> + -: 14:
>>> + -: 15:#pragma GCC suppress_coverage begin
>>> + #: 16: if (total != 45 && v == 1)
>>> + #: 17: printf ("Failure\n");
>>> + -: 18: else
>>> + #: 19: printf ("Success\n");
>>> + -: 20:#pragma GCC suppress_coverage end
>>> + 1: 21: return 0;
>>> + -: 22:@}
>>> +@end smallexample
>>> +
>>> The execution counts are cumulative. If the example program were
>>> executed again without removing the @file{.gcda} file, the count
>>> for the
>>> number of times each line in the source was executed would be added to
>>> diff --git a/gcc/gcov-dump.cc b/gcc/gcov-dump.cc
>>> index 0f9992c60f1b..709b0449ab6c 100644
>>> --- a/gcc/gcov-dump.cc
>>> +++ b/gcc/gcov-dump.cc
>>> @@ -41,6 +41,7 @@ static void tag_arcs (const char *, unsigned, int,
>>> unsigned);
>>> static void tag_conditions (const char *, unsigned, int, unsigned);
>>> static void tag_paths (const char *, unsigned, int, unsigned);
>>> static void tag_lines (const char *, unsigned, int, unsigned);
>>> +static void tag_suppress (const char *, unsigned, int, unsigned);
>>> static void tag_counters (const char *, unsigned, int, unsigned);
>>> static void tag_summary (const char *, unsigned, int, unsigned);
>>> extern int main (int, char **);
>>> @@ -82,6 +83,7 @@ static const tag_format_t tag_table[] =
>>> {GCOV_TAG_CONDS, "CONDITIONS", tag_conditions},
>>> {GCOV_TAG_PATHS, "PATHS", tag_paths},
>>> {GCOV_TAG_LINES, "LINES", tag_lines},
>>> + {GCOV_TAG_SUPPRESS, "SUPPRESS", tag_suppress},
>>> {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
>>> {0, NULL, NULL}
>>> };
>>> @@ -473,6 +475,26 @@ tag_lines (const char *filename ATTRIBUTE_UNUSED,
>>> }
>>> }
>>> +static void
>>> +tag_suppress (const char *filename ATTRIBUTE_UNUSED,
>>> + unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
>>> + unsigned depth)
>>> +{
>>> + if (flag_dump_contents)
>>> + {
>>> + unsigned nblocks = GCOV_TAG_SUPPRESS_NUM (length);
>>> + printf (" %u blocks suppressed", nblocks);
>>> + for (unsigned i = 0; i != nblocks; ++i)
>>> + {
>>> + gcov_position_t position = gcov_position ();
>>> + unsigned blockno = gcov_read_unsigned ();
>>> + printf ("\n");
>>> + print_prefix (filename, depth, position);
>>> + printf (VALUE_PADDING_PREFIX "block %u", blockno);
>>> + }
>>> + }
>>> +}
>>> +
>>> static void
>>> tag_counters (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 5a0b109c49b4..d6ac2651778a 100644
>>> --- a/gcc/gcov-io.h
>>> +++ b/gcc/gcov-io.h
>>> @@ -268,6 +268,9 @@ typedef uint64_t gcov_type_unsigned;
>>> #define GCOV_TAG_PATHS_LENGTH(NUM) ((NUM) * GCOV_WORD_SIZE)
>>> #define GCOV_TAG_PATHS_NUM(LENGTH) (((LENGTH) / GCOV_WORD_SIZE))
>>> #define GCOV_TAG_LINES ((gcov_unsigned_t)0x01450000)
>>> +#define GCOV_TAG_SUPPRESS ((gcov_unsigned_t)0x01510000)
>>> +#define GCOV_TAG_SUPPRESS_LENGTH(NUM) ((NUM) * GCOV_WORD_SIZE)
>>> +#define GCOV_TAG_SUPPRESS_NUM(LENGTH) ((LENGTH / GCOV_WORD_SIZE))
>>> #define GCOV_TAG_COUNTER_BASE ((gcov_unsigned_t)0x01a10000)
>>> #define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE)
>>> #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH / GCOV_WORD_SIZE) / 2)
>>> diff --git a/gcc/gcov.cc b/gcc/gcov.cc
>>> index 9797ddd8ca8d..87deeadcc756 100644
>>> --- a/gcc/gcov.cc
>>> +++ b/gcc/gcov.cc
>>> @@ -126,6 +126,9 @@ struct arc_info
>>> /* Is a false arc. */
>>> unsigned int false_value : 1;
>>> + /* Is suppressed arc by #pragma GCC suppress_coverage. */
>>> + unsigned int suppressed : 1;
>>> +
>>> /* Links to next arc on src and dst lists. */
>>> struct arc_info *succ_next;
>>> struct arc_info *pred_next;
>>> @@ -146,15 +149,42 @@ public:
>>> path is covered. */
>>> vector<gcov_type_unsigned> covered;
>>> + /* The prime paths after #pragma GCC suppress_coverage has been
>>> taken into
>>> + account. This is empty unless something is suppressed, in
>>> which case it
>>> + should be smaller than PATHS. The paths are lexicographically
>>> sorted. */
>>> + vector<vector<unsigned>> residual_paths;
>>> +
>>> + /* The covered paths after #pragma GCC suppress_coverage has been
>>> taken into
>>> + account. Like with COVERED, the bit N is set if the Nth path in
>>> + RESIDUAL_PATHS is covered. */
>>> + vector<gcov_type_unsigned> residual_covered;
>>> +
>>> /* The size (in bits) of each bucket. */
>>> static const size_t
>>> bucketsize = sizeof (gcov_type_unsigned) * BITS_PER_UNIT;
>>> - /* Count the covered paths. */
>>> + /* Helper for getting the right path set. */
>>> + const vector<vector<unsigned>>& get_paths () const
>>> + { return !suppressed_p () ? paths : residual_paths; }
>>> +
>>> + /* Get the number of paths, accounting for suppressed blocks. */
>>> + size_t path_count () const
>>> + { return get_paths ().size (); }
>>> +
>>> + /* Get the number of suppressed paths. This is 0 unless there is
>>> a #pragma
>>> + GCC suppress_coverage somewhere. */
>>> + size_t suppressed_count () const
>>> + { return paths.size () - path_count (); }
>>> +
>>> + /* Check if any paths suppressed by #pragma GCC
>>> suppress_coverage. */
>>> + bool suppressed_p () const
>>> + { return !residual_paths.empty (); }
>>> +
>>> + /* Count the covered paths, accounting for #pragma GCC
>>> suppress_coverage. */
>>> unsigned covered_paths () const
>>> {
>>> unsigned cnt = 0;
>>> - for (gcov_type_unsigned v : covered)
>>> + for (gcov_type_unsigned v : (!suppressed_p () ? covered :
>>> residual_covered))
>>> cnt += popcount_hwi (v);
>>> return cnt;
>>> }
>>> @@ -164,10 +194,14 @@ public:
>>> {
>>> if (covered.empty ())
>>> return false;
>>> +
>>> + const auto& cov = !suppressed_p () ? covered : residual_covered;
>>> const size_t bucket = n / bucketsize;
>>> const uint64_t bit = n % bucketsize;
>>> - return covered[bucket] & (gcov_type_unsigned (1) << bit);
>>> + return cov[bucket] & (gcov_type_unsigned (1) << bit);
>>> }
>>> +
>>> + void suppress_blocks (const vector<bool>& suppressed);
>>> };
>>> /* Describes which locations (lines and files) are associated with
>>> @@ -244,6 +278,9 @@ public:
>>> /* Block is a landing pad for longjmp or throw. */
>>> unsigned is_nonlocal_return : 1;
>>> + /* Block is suppressed by #pragma GCC suppress_coverage. */
>>> + unsigned suppressed : 1;
>>> +
>>> condition_info conditions;
>>> vector<block_location_info> locations;
>>> @@ -266,7 +303,7 @@ public:
>>> block_info::block_info (): succ (NULL), pred (NULL), num_succ (0),
>>> num_pred (0),
>>> id (0), count (0), count_valid (0), valid_chain (0),
>>> invalid_chain (0),
>>> exceptional (0), is_call_site (0), is_call_return (0),
>>> is_nonlocal_return (0),
>>> - locations (), chain (NULL)
>>> + suppressed (0), locations (), chain (NULL)
>>> {
>>> cycle.arc = NULL;
>>> }
>>> @@ -295,10 +332,12 @@ public:
>>> unsigned exists : 1;
>>> unsigned unexceptional : 1;
>>> unsigned has_unexecuted_block : 1;
>>> + /* Suppressed by #pragma GCC suppress_coverage. */
>>> + unsigned suppressed : 1;
>>> };
>>> line_info::line_info (): count (0), branches (), blocks (), exists
>>> (false),
>>> - unexceptional (0), has_unexecuted_block (0)
>>> + unexceptional (0), has_unexecuted_block (0), suppressed (0)
>>> {
>>> }
>>> @@ -385,6 +424,11 @@ public:
>>> /* Next function. */
>>> class function_info *next;
>>> + /* Blocks suppressed by #pragma GCC suppress_coverage. If any
>>> block is
>>> + suppressed this is non-empty, and the Nth bit is true if N is
>>> suppressed.
>>> + If suppressed_blocks[0] is true, the whole function is
>>> suppressed. */
>>> + vector<bool> suppressed_blocks;
>>> +
>>> /* Get demangled name of a function. The demangled name
>>> is converted when it is used for the first time. */
>>> char *get_demangled_name ()
>>> @@ -410,6 +454,11 @@ public:
>>> {
>>> return blocks.size () - 2;
>>> }
>>> +
>>> + bool suppressed_p () const
>>> + {
>>> + return !suppressed_blocks.empty () && suppressed_blocks.front ();
>>> + }
>>> };
>>> /* Function info comparer that will sort functions according to
>>> starting
>>> @@ -430,23 +479,30 @@ struct function_line_start_cmp
>>> struct coverage_info
>>> {
>>> + int function_suppressed;
>>> +
>>> int lines;
>>> int lines_executed;
>>> + int lines_suppressed;
>>> int branches;
>>> int branches_executed;
>>> int branches_taken;
>>> + int branches_suppressed;
>>> int conditions;
>>> int conditions_covered;
>>> + int conditions_suppressed;
>>> int calls;
>>> int calls_executed;
>>> + int calls_suppressed;
>>> char *name;
>>> unsigned paths;
>>> unsigned paths_covered;
>>> + unsigned paths_suppressed;
>>> };
>>> /* Describes a file mentioned in the block graph. Contains an array
>>> @@ -608,6 +664,7 @@ static unsigned object_runs;
>>> static unsigned total_lines;
>>> static unsigned total_executed;
>>> +static unsigned total_suppressed;
>>> /* Modification time of graph file. */
>>> @@ -814,7 +871,7 @@ static void add_branch_counts (coverage_info *,
>>> const arc_info *);
>>> static void add_condition_counts (coverage_info *, const block_info
>>> *);
>>> static void add_path_counts (coverage_info &, const function_info &);
>>> static void add_line_counts (coverage_info *, function_info *);
>>> -static void executed_summary (unsigned, unsigned);
>>> +static void executed_summary (unsigned, unsigned, unsigned);
>>> static void function_summary (const coverage_info *);
>>> static void file_summary (const coverage_info *);
>>> static const char *format_gcov (gcov_type, gcov_type, int);
>>> @@ -861,15 +918,221 @@ bool function_info::group_line_p (unsigned n,
>>> unsigned src_idx)
>>> return is_group && src == src_idx && start_line <= n && n <=
>>> end_line;
>>> }
>>> -/* Find the arc that connects BLOCK to the block with id DEST. This
>>> - edge must exist. */
>>> -static const arc_info&
>>> +/* Check if the block ID is a tombstone. */
>>> +static bool
>>> +tombstone_p (unsigned id)
>>> +{
>>> + return id == unsigned (-1);
>>> +};
>>> +
>>> +/* Remove tombstones from VEC. Preserves the order of remaining
>>> values. */
>>> +static vector<unsigned>
>>> +remove_tombstones (vector<unsigned> vec)
>>> +{
>>> + vec.erase (remove_if (vec.begin (), vec.end (), tombstone_p),
>>> vec.end ());
>>> + return vec;
>>> +}
>>> +
>>> +/* Check if SUB is a a proper contiguous subsequence of SUPER with
>>> tombstones
>>> + functioning as wildcards.
>>> +
>>> + If SUB and SUPER would be equal if tombstones are removed, SUB is
>>> not a
>>> + proper subsequence and this function returns false.
>>> +
>>> + Examples:
>>> +
>>> + SUB: 2 -1 12
>>> + SUPER: 2 -1 -1 12
>>> + Returns false because both sequences become [2 12] without
>>> tombstones.
>>> +
>>> + SUB: 2 -1 12
>>> + SUPER: 2 7 12
>>> + Returns true because 2 12 appear in that order in SUPER and there
>>> is a
>>> + tombstone between 2 and 12.
>>> +
>>> + SUB: 2 12
>>> + SUPER: 2 7 12
>>> + Returns false because 2 and 12 are not consecutive in SUPER.
>>> +
>>> + SUB: 12 7
>>> + SUPER: 2 7 12
>>> + Returns false because 7 is before 12 in SUPER.
>>> +
>>> + SUB: -1 7 12
>>> + SUPER: -1 7 -1 12 -1
>>> + Returns false because both sequences are 7 12 once tombstones are
>>> removed.
>>> +*/
>>> +static bool
>>> +tombstone_subsequence_p (const vector<unsigned>& sub,
>>> + const vector<unsigned>& super)
>>> +{
>>> + if (&sub == &super)
>>> + return false;
>>> +
>>> + auto xend = sub.end ();
>>> + auto yend = super.end ();
>>> + auto xitr = find_if_not (sub.begin (), xend, tombstone_p);
>>> + auto yitr = find_if_not (super.begin (), yend, tombstone_p);
>>> +
>>> + /* If SUB is empty or all tombstones it is included in any other
>>> path. */
>>> + if (xitr == xend)
>>> + return true;
>>> + /* If SUPER is empty or all tombstones it does not include
>>> anything. */
>>> + if (yitr == yend)
>>> + return false;
>>> +
>>> + bool equivalent = *yitr == *xitr;
>>> + /* Find the position in SUPER where the SUB may start. */
>>> + if (!equivalent)
>>> + {
>>> + yitr = find (yitr, yend, *xitr);
>>> + if (yitr == yend)
>>> + return false;
>>> + }
>>> +
>>> + for (; xitr != xend; ++xitr, ++yitr)
>>> + if (tombstone_p (*xitr))
>>> + {
>>> + /* Skip past any tombstones to find the next value. We need to
>>> compare
>>> + to the next non-tombstone value in SUPER to know if we
>>> skipped any
>>> + values to check for equivalence, otherwise this could just be
>>> + std::find for SUPER. */
>>> + xitr = find_if_not (xitr, xend, tombstone_p);
>>> + yitr = find_if_not (yitr, yend, tombstone_p);
>>> +
>>> + /* If there are no more non-tombstone blocks i SUB we're almost
>>> done,
>>> + but we still need to if there are more blocks in SUPER. */
>>> + if (xitr == xend)
>>> + return yitr != yend || !equivalent;
>>> +
>>> + if (yitr == yend)
>>> + return false;
>>> +
>>> + /* Now check for equivalence and look for the value in SUPER.
>>> This is
>>> + a no-op if we found it already. */
>>> + equivalent = equivalent && *yitr == *xitr;
>>> + yitr = find (yitr, yend, *xitr);
>>> + if (yitr == yend)
>>> + return false;
>>> + }
>>> + else if (*yitr != *xitr)
>>> + return false;
>>> +
>>> + yitr = find_if_not (yitr, yend, tombstone_p);
>>> + return yitr == yend && !equivalent;
>>> +}
>>> +
>>> +/* Check if NEEDLE is a proper subsequence of any sequence in
>>> HAYSTACK except
>>> + itself. Suppressed blocks/tombstones function as wildcards and
>>> match any
>>> + subsequence. If two sequences are equal once tombstones are
>>> removed they
>>> + are not proper subsequences of eachother.
>>> +
>>> + We may get odd sequence when we remove parts of a path, so we
>>> + extend the when the path A subsumes B to include non-contiguous
>>> + subsequences.
>>> +
>>> + Given a set of prime paths:
>>> + 2 3 4 12
>>> + 2 3 5 6 12
>>> + 2 3 5 7 8 10 12
>>> + 2 3 5 7 8 9 10 12
>>> + 2 3 5 7 8 9 11 12
>>> +
>>> + We have a blacklist of 3 4 5 6 8 9 10 which means these nodes
>>> should be
>>> + removed from all paths. If we replace blacklisted nodes with
>>> tombstones
>>> + (-1) and remove duplicates we get:
>>> + 2 -1 12
>>> + 2 -1 7 -1 11 12
>>> + 2 -1 7 -1 12
>>> +
>>> + A path is prime if it is not a subpath of any other paths.
>>> Suppressed
>>> + segments may be covered by any sequence of nodes, so the path:
>>> + 2 -1 7 -1 11 12
>>> + would subsume (<:) the other paths:
>>> + 2 -1 12 <: 2 [7 11] 12
>>> + 2 -1 7 12 <: 2 7 [11] 12
>>> +
>>> + Thus the only prime path is 2 7 11 12. */
>>> +static bool
>>> +subsumed_by_any_p (const vector<unsigned>& needle,
>>> + const vector<vector<unsigned>>& haystack)
>>> +{
>>> + if (all_of (needle.begin (), needle.end (), tombstone_p))
>>> + return true;
>>> + for (const auto& seq : haystack)
>>> + if (tombstone_subsequence_p (needle, seq))
>>> + return true;
>>> + return false;
>>> +}
>>> +
>>> +/* Compute the new paths and coverage by ignoring the blocks in
>>> SUPPRESSED.
>>> + Does nothing when SUPPRESSED is empty. This only adds the new
>>> + interpretation and does not change the observed path and coverage
>>> info. */
>>> +void
>>> +path_info::suppress_blocks (const vector<bool>& suppressed)
>>> +{
>>> + if (suppressed.empty ())
>>> + return;
>>> +
>>> + const unsigned tombstone = unsigned (-1);
>>> + /* Clean up the paths by replacing suppressed blocks with
>>> tombstones. */
>>> + vector<vector<unsigned>> ipaths;
>>> + ipaths.reserve (paths.size ());
>>> + for (const auto& path : paths)
>>> + {
>>> + vector<unsigned> tmp;
>>> + tmp.reserve (path.size ());
>>> + for (auto v : path)
>>> + tmp.push_back (!suppressed[v] ? v : tombstone);
>>> + ipaths.push_back (std::move (tmp));
>>> + }
>>> +
>>> + /* Changing paths means some paths may turn into subpaths, so we
>>> find and
>>> + store the new prime paths mapped to the original indices for
>>> later.
>>> + The new paths are map both sorts the paths and filters duplicates
>>> + duplicates. */
>>> + map<vector<unsigned> /* path */, vector<size_t> /* indices */>
>>> nextpaths;
>>> + for (size_t i = 0; i != ipaths.size (); ++i)
>>> + if (!subsumed_by_any_p (ipaths[i], ipaths))
>>> + nextpaths[remove_tombstones (ipaths[i])].push_back (i);
>>> +
>>> + /* Record the coverage of the new paths. The new paths may be the
>>> result
>>> + of merging paths, and if either original path is covered then
>>> the merged
>>> + path should be covered. */
>>> + vector<gcov_type_unsigned> nextcovered;
>>> + const size_t nbits = path_info::bucketsize;
>>> + const size_t nbuckets = (nextpaths.size () + (nbits - 1)) / nbits;
>>> + nextcovered.resize (nbuckets);
>>> + std::size_t n = 0;
>>> + for (const auto& np : nextpaths)
>>> + {
>>> + const size_t bucket = n / bucketsize;
>>> + const uint64_t bit = n % bucketsize;
>>> + for (size_t index : np.second)
>>> + if (covered_p (index))
>>> + {
>>> + nextcovered[bucket] |= (gcov_type_unsigned (1) << bit);
>>> + break;
>>> + }
>>> + n++;
>>> + }
>>> + residual_covered.swap (nextcovered);
>>> +
>>> + /* Store the new paths. The map iteration outputs the paths
>>> + lexicographically ordered. */
>>> + for (auto& p : nextpaths)
>>> + residual_paths.push_back (std::move (p.first));
>>> +}
>>> +
>>> +/* Find the arc that connects BLOCK to the block with id DEST, or
>>> nullptr if it
>>> + doesn't exist. */
>>> +static const arc_info*
>>> find_arc (const block_info &block, unsigned dest)
>>> {
>>> for (const arc_info *arc = block.succ; arc; arc = arc->succ_next)
>>> if (arc->dst->id == dest)
>>> - return *arc;
>>> - gcc_assert (false);
>>> + return arc;
>>> + return nullptr;
>>> }
>>> /* Cycle detection!
>>> @@ -1079,7 +1342,7 @@ main (int argc, char **argv)
>>> }
>>> if (!flag_use_stdout)
>>> - executed_summary (total_lines, total_executed);
>>> + executed_summary (total_lines, total_executed, total_suppressed);
>>> return return_code;
>>> }
>>> @@ -1384,7 +1647,11 @@ output_intermediate_json_line (json::array
>>> *object,
>>> for (it = line->branches.begin (); it != line->branches.end ();
>>> it++)
>>> {
>>> - if (!(*it)->is_unconditional && !(*it)->is_call_non_return)
>>> + if ((*it)->suppressed)
>>> + {
>>> + /* Skip. */
>>> + }
>>> + else if (!(*it)->is_unconditional && !(*it)->is_call_non_return)
>>> {
>>> json::object *branch = new json::object ();
>>> branch->set_integer ("count", (*it)->count);
>>> @@ -1412,6 +1679,9 @@ output_intermediate_json_line (json::array
>>> *object,
>>> vector<block_info *>::const_iterator it;
>>> for (it = line->blocks.begin (); it != line->blocks.end (); it++)
>>> {
>>> + if ((*it)->suppressed)
>>> + continue;
>>> +
>>> const condition_info& info = (*it)->conditions;
>>> if (info.n_terms == 0)
>>> continue;
>>> @@ -1518,12 +1788,14 @@ static void
>>> json_set_prime_path_coverage (json::object &function, function_info
>>> &info)
>>> {
>>> json::array *jpaths = new json::array ();
>>> - function.set_integer ("total_prime_paths", info.paths.paths.size ());
>>> + function.set_integer ("total_prime_paths", info.paths.path_count ());
>>> function.set_integer ("covered_prime_paths",
>>> info.paths.covered_paths ());
>>> + function.set_integer ("suppressed_prime_paths",
>>> + info.paths.suppressed_count ());
>>> function.set ("prime_path_coverage", jpaths);
>>> size_t pathno = 0;
>>> - for (const vector<unsigned> &path : info.paths.paths)
>>> + for (const vector<unsigned> &path : info.paths.get_paths ())
>>> {
>>> if (info.paths.covered_p (pathno++))
>>> continue;
>>> @@ -1544,14 +1816,16 @@ json_set_prime_path_coverage (json::object
>>> &function, function_info &info)
>>> const char *edge_kind = "";
>>> if (i + 1 != path.size ())
>>> {
>>> - const arc_info &arc = find_arc (block, path[i+1]);
>>> - if (arc.true_value)
>>> + const arc_info *arc = find_arc (block, path[i+1]);
>>> + if (!arc)
>>> + edge_kind = "suppress";
>>> + else if (arc->true_value)
>>> edge_kind = "true";
>>> - else if (arc.false_value)
>>> + else if (arc->false_value)
>>> edge_kind = "false";
>>> - else if (arc.fall_through)
>>> + else if (arc->fall_through)
>>> edge_kind = "fallthru";
>>> - else if (arc.is_throw)
>>> + else if (arc->is_throw)
>>> edge_kind = "throw";
>>> }
>>> @@ -1607,6 +1881,9 @@ output_json_intermediate_file (json::array
>>> *json_files, source_info *src)
>>> function->set_integer ("end_column", (*it)->end_column);
>>> function->set_integer ("blocks", (*it)->get_block_count ());
>>> function->set_integer ("blocks_executed", (*it)-
>>> >blocks_executed);
>>> + function->set_integer ("blocks_suppressed",
>>> + count ((*it)->suppressed_blocks.begin (),
>>> + (*it)->suppressed_blocks.end (), true));
>>> function->set_integer ("execution_count", (*it)-
>>> >blocks[0].count);
>>> json_set_prime_path_coverage (*function, **it);
>>> @@ -1770,6 +2047,19 @@ process_all_functions (void)
>>> function_info *fn = *it;
>>> unsigned src = fn->src;
>>> + if (!fn->suppressed_blocks.empty ())
>>> + {
>>> + /* Set the ignore flag on blocks, arcs. */
>>> + for (block_info &b : fn->blocks)
>>> + if (fn->suppressed_blocks[b.id] || fn->suppressed_p ())
>>> + {
>>> + b.suppressed = 1;
>>> + for (arc_info *arc = b.succ; arc; arc = arc->succ_next)
>>> + arc->suppressed = 1;
>>> + for (arc_info *arc = b.pred; arc; arc = arc->pred_next)
>>> + arc->suppressed = 1;
>>> + }
>>> + }
>>> if (!fn->counts.empty () || no_data_file)
>>> {
>>> source_info *s = &sources[src];
>>> @@ -1807,6 +2097,10 @@ process_all_functions (void)
>>> }
>>> }
>>> }
>>> +
>>> + if (block->suppressed || fn->suppressed_p ())
>>> + for (unsigned ln : block->locations[i].lines)
>>> + s->lines[ln].suppressed = 1;
>>> }
>>> }
>>> @@ -1821,12 +2115,22 @@ process_all_functions (void)
>>> if (fn->is_group)
>>> fn->lines.resize (fn->end_line - fn->start_line + 1);
>>> + /* Propagate the suppressed flag too. */
>>> + if (fn->is_group)
>>> + {
>>> + const auto& source = sources[fn->src];
>>> + for (unsigned ln = fn->start_line, dst = 0; ln <= fn-
>>> >end_line;
>>> + ++ln, ++dst)
>>> + fn->lines[dst].suppressed = source.lines.at (ln).suppressed;
>>> + }
>>> +
>>> solve_flow_graph (fn);
>>> if (fn->has_catch)
>>> find_exception_blocks (fn);
>>> /* For path coverage. */
>>> find_prime_paths (fn);
>>> + fn->paths.suppress_blocks (fn->suppressed_blocks);
>>> }
>>> else
>>> {
>>> @@ -1883,6 +2187,8 @@ generate_results (const char *file_name)
>>> coverage_info coverage;
>>> memset (&coverage, 0, sizeof (coverage));
>>> + if (fn->suppressed_p ())
>>> + coverage.function_suppressed = 1;
>>> coverage.name = fn->get_name ();
>>> add_line_counts (flag_function_summary ? &coverage : NULL, fn);
>>> @@ -1953,6 +2259,7 @@ generate_results (const char *file_name)
>>> file_summary (&src->coverage);
>>> total_lines += src->coverage.lines;
>>> total_executed += src->coverage.lines_executed;
>>> + total_suppressed += src->coverage.lines_suppressed;
>>> if (flag_gcov_file)
>>> {
>>> if (flag_json_format)
>>> @@ -2337,6 +2644,7 @@ read_graph_file (void)
>>> arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
>>> arc->true_value = !!(flags & GCOV_ARC_TRUE);
>>> arc->false_value = !!(flags & GCOV_ARC_FALSE);
>>> + arc->suppressed = 0;
>>> arc->succ_next = src_blk->succ;
>>> src_blk->succ = arc;
>>> @@ -2383,6 +2691,21 @@ read_graph_file (void)
>>> }
>>> }
>>> }
>>> + else if (fn && tag == GCOV_TAG_SUPPRESS)
>>> + {
>>> + const unsigned nblocks = GCOV_TAG_SUPPRESS_NUM (length);
>>> + if (!fn->suppressed_blocks.empty ())
>>> + fnotice (stderr, "%s:already seen suppressed blocks for
>>> '%s'\n",
>>> + bbg_file_name, fn->get_name ());
>>> + fn->suppressed_blocks.resize (fn->blocks.size (), false);
>>> + for (unsigned i = 0; i != nblocks; ++i)
>>> + {
>>> + const unsigned idx = gcov_read_unsigned ();
>>> + if (idx >= fn->blocks.size ())
>>> + goto corrupt;
>>> + fn->suppressed_blocks[idx] = true;
>>> + }
>>> + }
>>> else if (fn && tag == GCOV_TAG_CONDS)
>>> {
>>> unsigned num_dests = GCOV_TAG_CONDS_NUM (length);
>>> @@ -2960,16 +3283,20 @@ add_branch_counts (coverage_info *coverage,
>>> const arc_info *arc)
>>> if (arc->is_call_non_return)
>>> {
>>> coverage->calls++;
>>> - if (arc->src->count)
>>> + if (arc->suppressed)
>>> + coverage->calls_suppressed++;
>>> + else if (arc->src->count)
>>> coverage->calls_executed++;
>>> }
>>> else if (!arc->is_unconditional)
>>> {
>>> coverage->branches++;
>>> - if (arc->src->count)
>>> + if (arc->src->count && !arc->suppressed)
>>> coverage->branches_executed++;
>>> - if (arc->count)
>>> + if (arc->count && !arc->suppressed)
>>> coverage->branches_taken++;
>>> + if (arc->suppressed)
>>> + coverage->branches_suppressed++;
>>> }
>>> }
>>> @@ -2979,7 +3306,10 @@ 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 ();
>>> + if (block->suppressed)
>>> + coverage->conditions_suppressed += 2 * block->conditions.n_terms;
>>> + else
>>> + coverage->conditions_covered += block->conditions.popcount ();
>>> }
>>> /* Increment path totals, number of paths and number of covered paths,
>>> @@ -2988,8 +3318,9 @@ add_condition_counts (coverage_info *coverage,
>>> const block_info *block)
>>> static void
>>> add_path_counts (coverage_info &coverage, const function_info &fn)
>>> {
>>> - coverage.paths += fn.paths.paths.size ();
>>> + coverage.paths += fn.paths.path_count ();
>>> coverage.paths_covered += fn.paths.covered_paths ();
>>> + coverage.paths_suppressed += fn.paths.suppressed_count ();
>>> }
>>> /* Format COUNT, if flag_human_readable_numbers is set, return it
>>> human
>>> @@ -3048,11 +3379,15 @@ format_gcov (gcov_type top, gcov_type bottom,
>>> int decimal_places)
>>> /* Summary of execution */
>>> static void
>>> -executed_summary (unsigned lines, unsigned executed)
>>> +executed_summary (unsigned lines, unsigned executed, unsigned
>>> suppressed)
>>> {
>>> - if (lines)
>>> + if (lines && suppressed == 0)
>>> fnotice (stdout, "Lines executed:%s of %d\n",
>>> format_gcov (executed, lines, 2), lines);
>>> + else if (lines && suppressed > 0)
>>> + fnotice (stdout, "Lines executed:%s of %d (%d of %d suppressed)\n",
>>> + format_gcov (executed, lines - suppressed, 2), lines -
>>> suppressed,
>>> + suppressed, lines);
>>> else
>>> fnotice (stdout, "No executable lines\n");
>>> }
>>> @@ -3062,45 +3397,77 @@ executed_summary (unsigned lines, unsigned
>>> executed)
>>> static void
>>> function_summary (const coverage_info *coverage)
>>> {
>>> + if (coverage->function_suppressed)
>>> + {
>>> + fnotice (stdout, "Function '%s' suppressed\n", coverage->name);
>>> + return;
>>> + }
>>> fnotice (stdout, "%s '%s'\n", "Function", coverage->name);
>>> - executed_summary (coverage->lines, coverage->lines_executed);
>>> + executed_summary (coverage->lines, coverage->lines_executed,
>>> + coverage->lines_suppressed);
>>> if (coverage->branches)
>>> {
>>> - fnotice (stdout, "Branches executed:%s of %d\n",
>>> - format_gcov (coverage->branches_executed, coverage-
>>> >branches, 2),
>>> - coverage->branches);
>>> + const int branches = coverage->branches - coverage-
>>> >branches_suppressed;
>>> + if (coverage->branches_suppressed == 0)
>>> + fnotice (stdout, "Branches executed:%s of %d\n",
>>> + format_gcov (coverage->branches_executed, coverage->branches,
>>> + 2),
>>> + coverage->branches);
>>> + else
>>> + fnotice (stdout, "Branches executed:%s of %d (%d of %d
>>> suppressed)\n",
>>> + format_gcov (coverage->branches_executed, branches, 2),
>>> + branches, coverage->branches_suppressed, coverage->branches);
>>> fnotice (stdout, "Taken at least once:%s of %d\n",
>>> - format_gcov (coverage->branches_taken, coverage-
>>> >branches, 2),
>>> - coverage->branches);
>>> + format_gcov (coverage->branches_taken, branches, 2),
>>> branches);
>>> }
>>> else
>>> fnotice (stdout, "No branches\n");
>>> - if (coverage->calls)
>>> + if (coverage->calls && coverage->calls == 0)
>>> fnotice (stdout, "Calls executed:%s of %d\n",
>>> format_gcov (coverage->calls_executed, coverage->calls, 2),
>>> coverage->calls);
>>> + else if (coverage->calls && coverage->calls_suppressed > 0)
>>> + fnotice (stdout, "Calls executed:%s of %d (%d of %d suppressed)\n",
>>> + format_gcov (coverage->calls_executed, coverage->calls
>>> + - coverage->calls_suppressed, 2),
>>> + coverage->calls - coverage->calls_suppressed,
>>> + coverage->calls_suppressed, coverage->calls);
>>> else
>>> fnotice (stdout, "No calls\n");
>>> if (flag_conditions)
>>> {
>>> - if (coverage->conditions)
>>> + if (coverage->conditions && coverage->conditions_suppressed == 0)
>>> fnotice (stdout, "Condition outcomes covered:%s of %d\n",
>>> format_gcov (coverage->conditions_covered,
>>> coverage->conditions, 2),
>>> coverage->conditions);
>>> + if (coverage->conditions && coverage->conditions_suppressed > 0)
>>> + fnotice (stdout, "Condition outcomes covered:%s of %d"
>>> + " (%d of %d suppressed)\n",
>>> + format_gcov (coverage->conditions_covered,
>>> + coverage->conditions
>>> + - coverage->conditions_suppressed, 2),
>>> + coverage->conditions - coverage->conditions_suppressed,
>>> + coverage->conditions_suppressed, coverage->conditions);
>>> else
>>> fnotice (stdout, "No conditions\n");
>>> }
>>> if (flag_prime_paths)
>>> {
>>> - if (coverage->paths)
>>> + if (coverage->paths && coverage->paths_suppressed == 0)
>>> fnotice (stdout, "Prime paths covered:%s of %d\n",
>>> format_gcov (coverage->paths_covered, coverage->paths, 2),
>>> coverage->paths);
>>> + else if (coverage->paths && coverage->paths_suppressed > 0)
>>> + fnotice (stdout, "Prime paths covered:%s of %d (%u of %u
>>> suppressed)\n",
>>> + format_gcov (coverage->paths_covered, coverage->paths
>>> + - coverage->paths_suppressed, 2),
>>> + coverage->paths - coverage->paths_suppressed,
>>> + coverage->paths_suppressed, coverage->paths);
>>> else
>>> fnotice (stdout, "No path information\n");
>>> }
>>> @@ -3112,7 +3479,8 @@ static void
>>> file_summary (const coverage_info *coverage)
>>> {
>>> fnotice (stdout, "%s '%s'\n", "File", coverage->name);
>>> - executed_summary (coverage->lines, coverage->lines_executed);
>>> + executed_summary (coverage->lines, coverage->lines_executed,
>>> + coverage->lines_suppressed);
>>> if (flag_branches)
>>> {
>>> @@ -3322,7 +3690,9 @@ add_line_counts (coverage_info *coverage,
>>> function_info *fn)
>>> {
>>> if (!line->exists)
>>> coverage->lines++;
>>> - if (!line->count && block->count)
>>> + if (line->suppressed)
>>> + coverage->lines_suppressed++;
>>> + if (!line->count && block->count && !line->suppressed)
>>> coverage->lines_executed++;
>>> }
>>> line->exists = 1;
>>> @@ -3342,7 +3712,9 @@ add_line_counts (coverage_info *coverage,
>>> function_info *fn)
>>> {
>>> if (!line->exists)
>>> coverage->lines++;
>>> - if (!line->count && block->count)
>>> + if (!line->exists && line->suppressed)
>>> + coverage->lines_suppressed++;
>>> + if (!line->count && block->count && !line->suppressed)
>>> coverage->lines_executed++;
>>> }
>>> line->exists = 1;
>>> @@ -3429,7 +3801,9 @@ static void accumulate_line_info (line_info
>>> *line, source_info *src,
>>> if (line->exists && add_coverage)
>>> {
>>> src->coverage.lines++;
>>> - if (line->count)
>>> + if (line->suppressed)
>>> + src->coverage.lines_suppressed++;
>>> + if (line->count && !line->suppressed)
>>> src->coverage.lines_executed++;
>>> }
>>> }
>>> @@ -3482,7 +3856,9 @@ accumulate_line_counts (source_info *src)
>>> if (!src_line->exists)
>>> src->coverage.lines++;
>>> - if (!src_line->count && fn_line->count)
>>> + if (!src_line->exists && src_line->suppressed)
>>> + src->coverage.lines_suppressed++;
>>> + if (!src_line->count && fn_line->count && !src_line-
>>> >suppressed)
>>> src->coverage.lines_executed++;
>>> src_line->count += fn_line->count;
>>> @@ -3508,6 +3884,8 @@ output_conditions (FILE *gcov_file, const
>>> block_info *binfo)
>>> const condition_info& info = binfo->conditions;
>>> if (info.n_terms == 0)
>>> return;
>>> + if (binfo->suppressed)
>>> + return;
>>> const int expected = 2 * info.n_terms;
>>> const int got = info.popcount ();
>>> @@ -3535,7 +3913,9 @@ output_conditions (FILE *gcov_file, const
>>> block_info *binfo)
>>> static int
>>> output_branch_count (FILE *gcov_file, int ix, const arc_info *arc)
>>> {
>>> - if (arc->is_call_non_return)
>>> + if (arc->suppressed)
>>> + return 0;
>>> + else if (arc->is_call_non_return)
>>> {
>>> if (arc->src->count)
>>> {
>>> @@ -3610,13 +3990,14 @@ print_prime_path_lines (FILE *gcov_file,
>>> const function_info &fn,
>>> const char *edge_kind = "";
>>> if (k + 1 != path.size ())
>>> {
>>> - gcc_checking_assert (block.id == path[k]);
>>> - const arc_info &arc = find_arc (block, path[k+1]);
>>> - if (arc.true_value)
>>> + const arc_info *arc = find_arc (block, path[k+1]);
>>> + if (!arc)
>>> + edge_kind = "(suppress)";
>>> + else if (arc->true_value)
>>> edge_kind = "(true)";
>>> - else if (arc.false_value)
>>> + else if (arc->false_value)
>>> edge_kind = "(false)";
>>> - else if (arc.is_throw)
>>> + else if (arc->is_throw)
>>> edge_kind = "(throw)";
>>> }
>>> @@ -3680,12 +4061,14 @@ print_prime_path_source (FILE *gcov_file,
>>> const function_info &fn,
>>> const char *edge_kind = "";
>>> if (k + 1 != path.size ())
>>> {
>>> - const arc_info &arc = find_arc (block, path[k+1]);
>>> - if (arc.true_value)
>>> + const arc_info *arc = find_arc (block, path[k+1]);
>>> + if (!arc)
>>> + edge_kind = "(suppress)";
>>> + else if (arc->true_value)
>>> edge_kind = "(true)";
>>> - else if (arc.false_value)
>>> + else if (arc->false_value)
>>> edge_kind = "(false)";
>>> - else if (arc.is_throw)
>>> + else if (arc->is_throw)
>>> edge_kind = "(throw)";
>>> }
>>> @@ -3702,12 +4085,12 @@ print_prime_path_source (FILE *gcov_file,
>>> const function_info &fn,
>>> for (unsigned i = 0; i != loc.lines.size () - 1; ++i)
>>> {
>>> const unsigned line = loc.lines[i];
>>> - fprintf (gcov_file, "BB %2d: %-7s %3d", bb, "", line);
>>> + fprintf (gcov_file, "BB %2d: %-10s %3d", bb, "", line);
>>> print_source_line (gcov_file, lines, line);
>>> }
>>> const unsigned line = loc.lines.back ();
>>> - fprintf (gcov_file, "BB %2d: %-7s %3d", bb, edge_kind, line);
>>> + fprintf (gcov_file, "BB %2d: %-10s %3d", bb, edge_kind, line);
>>> print_source_line (gcov_file, lines, line);
>>> }
>>> }
>>> @@ -3725,8 +4108,16 @@ output_path_coverage (FILE *gcov_file, const
>>> function_info *fn)
>>> if (!flag_prime_paths)
>>> return 0;
>>> - if (fn->paths.paths.empty ())
>>> + const path_info& paths = fn->paths;
>>> + if (fn->paths.get_paths ().empty ())
>>> fnotice (gcov_file, "path coverage omitted\n");
>>> + else if (paths.suppressed_p ())
>>> + fnotice (gcov_file, "Prime paths covered %u of "
>>> HOST_SIZE_T_PRINT_UNSIGNED
>>> + " (" HOST_SIZE_T_PRINT_UNSIGNED " of "
>>> HOST_SIZE_T_PRINT_UNSIGNED
>>> + " suppressed)\n", fn->paths.covered_paths (),
>>> + (fmt_size_t)fn->paths.path_count (),
>>> + (fmt_size_t)fn->paths.suppressed_count (),
>>> + (fmt_size_t)fn->paths.paths.size ());
>>> else
>>> fnotice (gcov_file, "paths covered %u of "
>>> HOST_SIZE_T_PRINT_UNSIGNED "\n",
>>> fn->paths.covered_paths (), (fmt_size_t)fn-
>>> >paths.paths.size ());
>>> @@ -3734,14 +4125,14 @@ output_path_coverage (FILE *gcov_file, const
>>> function_info *fn)
>>> if (flag_prime_paths_lines_uncovered ||
>>> flag_prime_paths_lines_covered)
>>> {
>>> unsigned pathno = 0;
>>> - for (const vector<unsigned> &path : fn->paths.paths)
>>> + for (const vector<unsigned> &path : fn->paths.get_paths ())
>>> print_prime_path_lines (gcov_file, *fn, path, pathno++);
>>> }
>>> if (flag_prime_paths_source_uncovered ||
>>> flag_prime_paths_source_covered)
>>> {
>>> unsigned pathno = 0;
>>> - for (const vector<unsigned> &path : fn->paths.paths)
>>> + for (const vector<unsigned> &path : fn->paths.get_paths ())
>>> print_prime_path_source (gcov_file, *fn, path, pathno++);
>>> }
>>> return 1;
>>> @@ -3836,13 +4227,19 @@ pad_count_string (string &s)
>>> static void
>>> output_line_beginning (FILE *f, bool exists, bool unexceptional,
>>> bool has_unexecuted_block,
>>> + bool suppressed,
>>> gcov_type count, unsigned line_num,
>>> const char *exceptional_string,
>>> const char *unexceptional_string,
>>> unsigned int maximum_count)
>>> {
>>> string s;
>>> - if (exists)
>>> + if (suppressed)
>>> + {
>>> + s = "#";
>>> + pad_count_string (s);
>>> + }
>>> + else if (exists)
>>> {
>>> if (count > 0)
>>> {
>>> @@ -3935,6 +4332,7 @@ output_line_details (FILE *f, const line_info
>>> *line, unsigned line_num)
>>> {
>>> output_line_beginning (f, line->exists,
>>> (*it)->exceptional, false,
>>> + (*it)->suppressed,
>>> (*it)->count, line_num,
>>> "%%%%%", "$$$$$", 0);
>>> fprintf (f, "-block %d", (*it)->id);
>>> @@ -4092,14 +4490,15 @@ output_lines (FILE *gcov_file, const
>>> source_info *src)
>>> /* For lines which don't exist in the .bb file, print '-' before
>>> the source line. For lines which exist but were never
>>> - executed, print '#####' or '=====' before the source line.
>>> - Otherwise, print the execution count before the source line.
>>> - There are 16 spaces of indentation added before the source
>>> - line so that tabs won't be messed up. */
>>> + executed, print '#####' or '=====' before the source line. For
>>> lines
>>> + that were suppressed, print '#'. Otherwise, print the
>>> execution count
>>> + before the source line. There are 16 spaces of indentation added
>>> + before the source line so that tabs won't be messed up. */
>>> if (line_num <= filtered_line_end)
>>> {
>>> output_line_beginning (gcov_file, line->exists, line-
>>> >unexceptional,
>>> - line->has_unexecuted_block, line->count,
>>> + line->has_unexecuted_block, line->suppressed,
>>> + line->count,
>>> line_num, "=====", "#####",
>>> src->maximum_count);
>>> @@ -4138,13 +4537,14 @@ output_lines (FILE *gcov_file, const
>>> source_info *src)
>>> /* For lines which don't exist in the .bb file, print '-'
>>> before the source line. For lines which exist but
>>> were never executed, print '#####' or '=====' before
>>> - the source line. Otherwise, print the execution count
>>> - before the source line.
>>> - There are 16 spaces of indentation added before the source
>>> - line so that tabs won't be messed up. */
>>> + the source line. For suppressed lines, print '#'.
>>> + Otherwise, print the execution count before the source
>>> + line. There are 16 spaces of indentation added before the
>>> + source line so that tabs won't be messed up. */
>>> output_line_beginning (gcov_file, line->exists,
>>> line->unexceptional,
>>> line->has_unexecuted_block,
>>> + line->suppressed,
>>> line->count,
>>> l, "=====", "#####",
>>> src->maximum_count);
>>> diff --git a/gcc/profile.cc b/gcc/profile.cc
>>> index 17841fc723a0..5ae64d4043fd 100644
>>> --- a/gcc/profile.cc
>>> +++ b/gcc/profile.cc
>>> @@ -66,6 +66,8 @@ along with GCC; see the file COPYING3. If not see
>>> #include "cfgloop.h"
>>> #include "sreal.h"
>>> #include "file-prefix-map.h"
>>> +#include "stringpool.h"
>>> +#include "attribs.h"
>>> #include "profile.h"
>>> #include "auto-profile.h"
>>> @@ -1205,6 +1207,134 @@ read_thunk_profile (struct cgraph_node *node)
>>> return;
>>> }
>>> +/* Disable coverage for BB. This is used for #pragma GCC
>>> suppress_coverage. */
>>> +void
>>> +suppress_coverage (basic_block bb)
>>> +{
>>> + bb->flags |= BB_COVERAGE_SUPPRESSED;
>>> +}
>>> +
>>> +/* Unset the flag set by suppress_coverage. This is only useful
>>> when merging
>>> + blocks. */
>>> +void
>>> +suppress_coverage_unset (basic_block bb)
>>> +{
>>> + bb->flags &= ~BB_COVERAGE_SUPPRESSED;
>>> +}
>>> +
>>> +/* Check if BB has coverage disabled by #pragma GCC
>>> suppress_coverage. */
>>> +bool
>>> +coverage_suppressed_p (basic_block bb)
>>> +{
>>> + return bb->flags & BB_COVERAGE_SUPPRESSED;
>>> +}
>>> +
>>> +/* Check if any blocks are disabled by #pragma suppress_coverage in
>>> the current
>>> + function. */
>>> +static bool
>>> +any_block_coverage_suppressed_p ()
>>> +{
>>> + basic_block bb;
>>> + FOR_EACH_BB_FN (bb, cfun)
>>> + if (coverage_suppressed_p (bb))
>>> + return true;
>>> + return false;
>>> +}
>>> +
>>> +/* The source locations of #pragma GCC suppress_coverage begin/end.
>>> For each
>>> + entry, the source_range m_finish/m_end should be the (expanded)
>>> source
>>> + location of the begin/end. If there is no end, m_finish will be
>>> + UNKNOWN_LOCATION. */
>>> +static vec<source_range> suppress_coverage_ranges;
>>> +
>>> +/* Try to add LOC as the beginning of a new range. If a range was
>>> started
>>> + already, this is a no-op. Returns true if a new range was
>>> created. */
>>> +bool
>>> +suppress_coverage_begin (location_t loc)
>>> +{
>>> + if (!suppress_coverage_ranges.is_empty ()
>>> + && suppress_coverage_ranges.last ().m_finish == UNKNOWN_LOCATION)
>>> + return false;
>>> +
>>> + loc = get_pure_location (expansion_point_location (loc));
>>> + source_range range = source_range::from_locations (loc,
>>> UNKNOWN_LOCATION);
>>> + suppress_coverage_ranges.safe_push (range);
>>> + return true;
>>> +}
>>> +
>>> +/* Try to close the last range created by suppress_coverage_begin at
>>> LOC. If
>>> + the range has been closed already (or not opened), this is a no-
>>> op. Returns
>>> + true if a range was closed. */
>>> +bool
>>> +suppress_coverage_end (location_t loc)
>>> +{
>>> + if (suppress_coverage_ranges.is_empty ()
>>> + || suppress_coverage_ranges.last ().m_finish != UNKNOWN_LOCATION)
>>> + return false;
>>> + loc = get_pure_location (expansion_point_location (loc));
>>> + suppress_coverage_ranges.last ().m_finish = loc;
>>> + return true;
>>> +}
>>> +
>>> +/* Check if STMT is anchored to a line of code in a range disabled
>>> by #pragma
>>> + GCC suppress_coverage begin/end. This function always returns
>>> false if
>>> + coverage is disabled as it is the faster check, and nothing
>>> should be
>>> + suppressed anyway.
>>> +
>>> + If STMT is at an UNKNOWN_LOCATION or ADHOC_LOC, this function
>>> returns PREV.
>>> + This is probably a compiler-generated statement that should
>>> inherit the
>>> + disabled state of the previous statement since it is really tied
>>> to it, and
>>> + there is no opportunity for a #pragma in-between. */
>>> +bool
>>> +in_pragma_suppress_coverage_p (gimple* stmt, bool prev)
>>> +{
>>> + if (!coverage_instrumentation_p ())
>>> + return false;
>>> +
>>> + location_t loc = expansion_point_location (gimple_location (stmt));
>>> + if (loc == UNKNOWN_LOCATION || IS_ADHOC_LOC (loc))
>>> + return prev;
>>> +
>>> + return location_in_pragma_suppress_coverage_p (loc);
>>> +}
>>> +
>>> +/* Check if LOC is within a #pragma GCC suppress_coverage block. */
>>> +bool
>>> +location_in_pragma_suppress_coverage_p (location_t loc)
>>> +{
>>> + loc = get_pure_location (expansion_point_location (loc));
>>> + for (const source_range& dl : suppress_coverage_ranges)
>>> + if (linemap_location_before_p (line_table, dl.m_start, loc)
>>> + && (linemap_location_before_p (line_table, loc, dl.m_finish)
>>> + || dl.m_finish == UNKNOWN_LOCATION))
>>> + return true;
>>> + return false;
>>> +}
>>> +
>>> +/* Check if FN is fully between #pragma GCC suppress_coverage begin/
>>> end. In
>>> + that case we can disable the whole function rather than every
>>> block, and
>>> + omit MC/DC (-fcondition-coverage) and prime path coverage (-
>>> fpath- coverage)
>>> + instrumentation. */
>>> +static bool
>>> +fn_in_pragma_suppress_coverage_p (function *fn)
>>> +{
>>> + if (!coverage_instrumentation_p ())
>>> + return false;
>>> +
>>> + if (lookup_attribute ("gnu", "suppress_coverage",
>>> + DECL_ATTRIBUTES (fn->decl)))
>>> + return true;
>>> +
>>> + const location_t start = fn->function_start_locus;
>>> + const location_t end = fn->function_end_locus;
>>> +
>>> + for (const source_range& dl : suppress_coverage_ranges)
>>> + if (linemap_location_before_p (line_table, dl.m_start, start)
>>> + && (linemap_location_before_p (line_table, end, dl.m_finish)
>>> + || dl.m_finish == UNKNOWN_LOCATION))
>>> + return true;
>>> + return false;
>>> +}
>>> /* Instrument and/or analyze program behavior based on program the
>>> CFG.
>>> @@ -1478,6 +1608,8 @@ branch_prob (bool thunk)
>>> lineno_checksum = coverage_compute_lineno_checksum ();
>>> }
>>> + const bool fn_coverage_suppressed_p =
>>> fn_in_pragma_suppress_coverage_p (cfun);
>>> +
>>> /* Write the data from which gcov can reconstruct the basic block
>>> graph and function line numbers (the gcno file). */
>>> output_to_file = false;
>>> @@ -1538,6 +1670,27 @@ branch_prob (bool thunk)
>>> gcov_write_length (offset);
>>> }
>>> + /* Disabled blocks or function. Lines, arcs, path segments
>>> through
>>> + ignored blocks should not count towards coverage. Ignoring
>>> coverage
>>> + is a matter of interpretation and does not change the
>>> instrumentation;
>>> + gcov sorts it out. If the whole function is disabled (by the
>>> + attribute on the function, not the statements), the entry block is
>>> + recorded as ignored. */
>>> + if (fn_coverage_suppressed_p)
>>> + {
>>> + offset = gcov_write_tag (GCOV_TAG_SUPPRESS);
>>> + gcov_write_unsigned (ENTRY_BLOCK);
>>> + gcov_write_length (offset);
>>> + }
>>> + else if (any_block_coverage_suppressed_p ())
>>> + {
>>> + offset = gcov_write_tag (GCOV_TAG_SUPPRESS);
>>> + FOR_EACH_BB_FN (bb, cfun)
>>> + if (coverage_suppressed_p (bb))
>>> + gcov_write_unsigned (bb->index);
>>> + gcov_write_length (offset);
>>> + }
>>> +
>>> /* Line numbers. */
>>> /* Initialize the output. */
>>> output_location (&streamed_locations, NULL, 0, NULL, NULL);
>>> @@ -1612,7 +1765,7 @@ branch_prob (bool thunk)
>>> if (condition_coverage_flag || path_coverage_flag ||
>>> profile_arc_flag)
>>> gimple_init_gcov_profiler ();
>>> - if (condition_coverage_flag)
>>> + if (condition_coverage_flag && !fn_coverage_suppressed_p)
>>> {
>>> struct condcov *cov = find_conditions (cfun);
>>> gcc_assert (cov);
>>> @@ -1662,7 +1815,7 @@ branch_prob (bool thunk)
>>> }
>>> unsigned instrument_prime_paths (struct function*);
>>> - if (path_coverage_flag)
>>> + if (path_coverage_flag && !fn_coverage_suppressed_p)
>>> {
>>> const unsigned npaths = instrument_prime_paths (cfun);
>>> if (output_to_file)
>>> diff --git a/gcc/profile.h b/gcc/profile.h
>>> index 3c57a40c2c71..04f506af78aa 100644
>>> --- a/gcc/profile.h
>>> +++ b/gcc/profile.h
>>> @@ -81,4 +81,11 @@ extern struct gcov_summary *profile_info,
>>> *gcov_profile_info;
>>> -fcondition-coverage -fpath-coverage. */
>>> extern bool coverage_instrumentation_p ();
>>> +/* For #pragma GCC suppress_coverage begin/end. */
>>> +extern bool in_pragma_suppress_coverage_p (gimple*, bool);
>>> +extern bool location_in_pragma_suppress_coverage_p (location_t);
>>> +extern bool coverage_suppressed_p (basic_block);
>>> +extern void suppress_coverage (basic_block);
>>> +extern void suppress_coverage_unset (basic_block);
>>> +
>>> #endif /* PROFILE_H */
>>> diff --git a/gcc/testsuite/g++.dg/gcov/gcov-24.C b/gcc/testsuite/g+
>>> +.dg/gcov/gcov-24.C
>>> new file mode 100644
>>> index 000000000000..fa875a147878
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/gcov/gcov-24.C
>>> @@ -0,0 +1,1022 @@
>>> +/* { dg-options "--coverage" } */
>>> +/* { dg-do run } */
>>> +
>>> +/* testsuite/gcc.misc-tests/gcov-35.c, compiled with the C++
>>> frontend. */
>>> +
>>> +void noop () {}
>>> +
>>> +int do_something (int i) { return i; }
>>> +
>>> +/* Empty loop bodies should still disable coverage for the for (;;)
>>> and compile
>>> + fine. */
>>> +int
>>> +empty_body_for_loop ()
>>> +{
>>> + int i;
>>> +#pragma GCC suppress_coverage begin
>>> + for (i = 0; i < 10; i++) /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + ;
>>> + return i;
>>> +}
>>> +
>>> +/* Making the for (;;) multi line should report count per line. g++
>>> considers
>>> + the i++ unexecuted, while gcc counts it. */
>>> +int
>>> +ignored_for_loop ()
>>> +{
>>> + int i;
>>> +#pragma GCC suppress_coverage begin
>>> + for (i = 0; i < 10; i++) /* count(#) */
>>> + {
>>> + noop (); /* count(#) */
>>> + noop (); /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + /* Making the for (;;) multi line should report count per line. */
>>> +#pragma GCC suppress_coverage begin
>>> + for (i = 0; /* count(#) */
>>> + i < 20; /* count(#) */
>>> + i++) /* count(-) */
>>> + {
>>> + noop (); /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + noop ();
>>> + i++;
>>> +
>>> + return 0; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +declarations (int a)
>>> +{
>>> + // Should work when it is the first declaration
>>> +#pragma GCC suppress_coverage begin
>>> + int b = a + 1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + // This is ignored in C (no init) but may be implicitly
>>> initialized in C++ to
>>> + // an erroneous value, so this could be - (not executed) or #
>>> (ignored)
>>> + // depending on the -std= flag.
>>> +#pragma GCC suppress_coverage begin
>>> + int c;
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + a *= 2; /* count(1) */
>>> +
>>> + // Should work when it is not the first declaration
>>> +#pragma GCC suppress_coverage begin
>>> + int d = a - 1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + c = a+b+d; /* count(1) */
>>> + return c;
>>> +}
>>> +
>>> +int
>>> +compound_statements (int a)
>>> +{
>>> + int c;
>>> +#pragma GCC suppress_coverage begin
>>> + {
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + return c; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +while1 (int a)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + while (a > 0) /* count(#) */
>>> + a = do_something (a - 1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return a;
>>> +}
>>> +
>>> +/* If the pragma stops blocks from merging this would end up with
>>> the wrong
>>> + count. The while (cond) should run 6 times (a = 5), but earlier
>>> drafts
>>> + clocked it at 7 because an empty block at the end of the body
>>> would have
>>> + coverage suppressed, while the loop header did which prevented
>>> merging. */
>>> +int
>>> +while2 (int a)
>>> +{
>>> + while (a > 0) /* count(6) */
>>> +#pragma GCC suppress_coverage begin
>>> + a = do_something (a - 1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +dowhile1 (int a)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + do
>>> + {
>>> + a = do_something (a - 1); /* count(#) */
>>> + } while (a > 0); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +dowhile2 (int a)
>>> +{
>>> + do
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + a = do_something (a - 1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + } while (a > 0); /* count(5) */
>>> + return a;
>>> +}
>>> +
>>> +void
>>> +call_while ()
>>> +{
>>> + while1 (5);
>>> + while2 (5);
>>> + dowhile1 (5);
>>> + dowhile2 (5);
>>> +}
>>> +
>>> +/* Based on gcov-pr85217.c, a loop with both breaks and continues. */
>>> +int
>>> +for1 ()
>>> +{
>>> + int a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(2) */
>>> + {
>>> + break; /* count(1) */
>>> + a; /* count(-) */
>>> + continue; /* count(1) */
>>> + }
>>> + continue; /* count(1) */
>>> + }
>>> +
>>> + a = 0;
>>> +#pragma GCC suppress_coverage begin
>>> + for (;; a++) /* count(#) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(#) */
>>> + {
>>> + break; /* count(#) */
>>> + a; /* count(-) */
>>> + continue; /* count(#) */
>>> + }
>>> + continue; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> +#pragma GCC suppress_coverage begin
>>> + if (a) /* count(#) */
>>> + {
>>> + break; /* count(#) */
>>> + a; /* count(-) */
>>> + continue; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + continue; /* count(1) */
>>> + }
>>> +
>>> + a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(2) */
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + break; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + a; /* count(-) */
>>> + continue; /* count(1) */
>>> + }
>>> + continue; /* count(1) */
>>> + }
>>> +
>>> + a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(2) */
>>> + {
>>> + break; /* count(1) */
>>> + a; /* count(-) */
>>> +#pragma GCC suppress_coverage begin
>>> + continue; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + continue; /* count(1) */
>>> + }
>>> +
>>> + a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(2) */
>>> + {
>>> + break; /* count(1) */
>>> + a; /* count(-) */
>>> + continue; /* count(1) */
>>> + }
>>> +#pragma GCC suppress_coverage begin
>>> + continue; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +
>>> + return a;
>>> +}
>>> +
>>> +/* A loop with break. */
>>> +int
>>> +for2 (int n)
>>> +{
>>> + int acc = 0;
>>> + for (int i = 0; i < n; ++i) /* count(7) */
>>> + {
>>> + acc += do_something (i); /* count(7) */
>>> +#pragma GCC suppress_coverage begin
>>> + if (acc > 10) /* count(#) */
>>> + break; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + acc -= 1; /* count(6) */
>>> + }
>>> + return acc;
>>> +}
>>> +
>>> +int
>>> +for3 (int n)
>>> +{
>>> + int acc = 0;
>>> + for (int i = 0; i < n; ++i) /* count(7) */
>>> + {
>>> + acc += do_something (i); /* count(7) */
>>> + if (acc > 10) /* count(7) */
>>> + /* The break is a not-executable-line in C, but will be
>>> successfully be
>>> + ignored in C++. */
>>> +#pragma GCC suppress_coverage begin
>>> + break; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + acc -= 1; /* count(6) */
>>> + }
>>> + return acc;
>>> +}
>>> +
>>> +/* Based on the test in gcov-4.c */
>>> +int for_val1;
>>> +int for_temp;
>>> +int
>>> +nested_for1 (int m, int n, int o)
>>> +{
>>> + int i, j, k;
>>> + for_temp = 1; /* count(6) */
>>> + for (i = 0; i < n; i++) /* count(20) */
>>> + for (j = 0; j < m; j++) /* count(44) */
>>> +#pragma GCC suppress_coverage begin
>>> + for (k = 0; k < o; k++) /* count(#) */
>>> + for_temp++; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return for_temp; /* count(6) */
>>> +}
>>> +
>>> +void
>>> +call_for ()
>>> +{
>>> + for1 ();
>>> + for2 (10);
>>> + for3 (10);
>>> +
>>> + for_val1 += nested_for1 (0, 0, 0);
>>> + for_val1 += nested_for1 (1, 0, 0);
>>> + for_val1 += nested_for1 (1, 3, 0);
>>> + for_val1 += nested_for1 (1, 3, 1);
>>> + for_val1 += nested_for1 (3, 1, 5);
>>> + for_val1 += nested_for1 (3, 7, 3);
>>> +}
>>> +
>>> +int ifelse_val1;
>>> +int ifelse_val2;
>>> +int ifelse_val3;
>>> +
>>> +int
>>> +test_ifelse1 (int i, int j)
>>> +{
>>> + int result = 0;
>>> + /* We can ignore the THEN. */
>>> + if (i) /* count(5) */
>>> + if (j) /* count(3) */
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (4); /* count(#) */
>>> + else
>>> +#pragma GCC suppress_coverage end
>>> + result = do_something (1024);
>>> + /* We can ignore the ELSE. */
>>> + else
>>> + if (j) /* count(2) */
>>> + result = do_something (1); /* count(1) */
>>> + else
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (2); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + if (i > j) /* count(5) */
>>> + result = do_something (result*2); /* count(1) */
>>> +
>>> + /* We can ignore the whole if-then-else. */
>>> + if (i > 10) /* count(5) */
>>> +#pragma GCC suppress_coverage begin
>>> + if (j > 10) /* count(#) */
>>> + result = do_something (result*4); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return result; /* count(5) */
>>> +}
>>> +
>>> +int
>>> +test_ifelse2 (int i)
>>> +{
>>> + int result = 0;
>>> +#pragma GCC suppress_coverage begin
>>> + if (!i) /* count(#) */
>>> + result = do_something (1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + if (i == 1) /* count(6) */
>>> + result = do_something (1024);
>>> +
>>> + if (i == 2) /* count(6) */
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (2); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + if (i == 3) /* count(6) */
>>> +#pragma GCC suppress_coverage begin
>>> + return do_something (8); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + if (i == 4) /* count(#) */
>>> + return do_something (2048); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return result; /* count(4) */
>>> +}
>>> +
>>> +int
>>> +test_ifelse3 (int i, int j)
>>> +{
>>> + int result = 1;
>>> + /* Multi-condition ifs are suppressed, too */
>>> +#pragma GCC suppress_coverage begin
>>> + if (i > 10 && j > i && j < 20) /* count(#) */
>>> + result = do_something (16); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + if (i == 3 || j == 47 || i == j) /* count(11) */
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (64); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return result; /* count(11) */
>>> +}
>>> +
>>> +/* These are based on gcov-17.c */
>>> +int
>>> +test_ifelse4 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> +#pragma GCC suppress_coverage begin
>>> + if (true_var) /* count(#) */
>>> + {
>>> + if (false_var) /* count(#) */
>>> + ret = 111; /* count(#) */
>>> + }
>>> + else
>>> + ret = 999; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse5 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + if (true_var) /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + {
>>> + if (false_var) /* count(#) */
>>> + ret = 111; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + else
>>> + ret = 999; /* count(#####) */
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse6 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + if (true_var) /* count(1) */
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + if (false_var) /* count(#) */
>>> + ret = 111; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + else
>>> + ret = 999; /* count(#####) */
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse7 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + if (true_var) /* count(1) */
>>> + {
>>> + if (false_var) /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + ret = 111; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + else
>>> + ret = 999; /* count(#####) */
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse8 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + if (true_var) /* count(1) */
>>> + {
>>> + if (false_var) /* count(1) */
>>> + ret = 111; /* count(#####) */
>>> + }
>>> + else
>>> +#pragma GCC suppress_coverage begin
>>> + ret = 999; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse9 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + /* With the pragma we can disable condition, but still observe the
>>> then-else. */
>>> +#pragma GCC suppress_coverage begin
>>> + if (true_var) /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + {
>>> + if (false_var) /* count(1) */
>>> + ret = 111; /* count(#####) */
>>> + }
>>> + else
>>> + ret = 999; /* count(#####) */
>>> + return ret;
>>> +}
>>> +
>>> +void
>>> +call_ifelse ()
>>> +{
>>> + ifelse_val1 += test_ifelse1 (0, 2);
>>> + ifelse_val1 += test_ifelse1 (0, 0);
>>> + ifelse_val1 += test_ifelse1 (1, 2);
>>> + ifelse_val1 += test_ifelse1 (10, 2);
>>> + ifelse_val1 += test_ifelse1 (11, 11);
>>> +
>>> + ifelse_val2 += test_ifelse2 (0);
>>> + ifelse_val2 += test_ifelse2 (2);
>>> + ifelse_val2 += test_ifelse2 (2);
>>> + ifelse_val2 += test_ifelse2 (2);
>>> + ifelse_val2 += test_ifelse2 (3);
>>> + ifelse_val2 += test_ifelse2 (3);
>>> +
>>> + ifelse_val3 += test_ifelse3 (11, 19);
>>> + ifelse_val3 += test_ifelse3 (25, 27);
>>> + ifelse_val3 += test_ifelse3 (11, 22);
>>> + ifelse_val3 += test_ifelse3 (11, 10);
>>> + ifelse_val3 += test_ifelse3 (21, 32);
>>> + ifelse_val3 += test_ifelse3 (21, 20);
>>> + ifelse_val3 += test_ifelse3 (1, 2);
>>> + ifelse_val3 += test_ifelse3 (32, 31);
>>> + ifelse_val3 += test_ifelse3 (3, 0);
>>> + ifelse_val3 += test_ifelse3 (0, 47);
>>> + ifelse_val3 += test_ifelse3 (65, 65);
>>> +
>>> + test_ifelse4 (1, 0);
>>> + test_ifelse5 (1, 0);
>>> + test_ifelse6 (1, 0);
>>> + test_ifelse7 (1, 0);
>>> + test_ifelse8 (1, 0);
>>> + test_ifelse9 (1, 0);
>>> +}
>>> +
>>> +int switch_val, switch_m;
>>> +int
>>> +test_switch (int i, int j)
>>> +{
>>> + int result = 0; /* count(5) */
>>> + /* We can disable individual statements and breaks in the switch. */
>>> + switch (i) /* count(5) */
>>> + {
>>> + case 1:
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (2); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + break; /* count(1) */
>>> + case 2:
>>> + result = do_something (1024);
>>> + break;
>>> + case 3:
>>> + case 4:
>>> + if (j == 2) /* count(3) */
>>> + return do_something (4); /* count(1) */
>>> + result = do_something (8); /* count(2) */
>>> +#pragma GCC suppress_coverage begin
>>> + break; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + default:
>>> + result = do_something (32); /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + switch_m++; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + break;
>>> + }
>>> +
>>> + /* We can disable the whole switch. */
>>> +#pragma GCC suppress_coverage begin
>>> + switch (i)
>>> + {
>>> + case 1:
>>> + result = do_something (64); /* count(#) */
>>> + break; /* count(#) */
>>> + case 2:
>>> + result = do_something (128); /* count(#) */
>>> + break; /* count(#) */
>>> + case 3:
>>> + result = do_something (256); /* count(#) */
>>> + break; /* count(#) */
>>> + default:
>>> + result = do_something (512); /* count(#) */
>>> + switch_m++; /* count(#) */
>>> + break;
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return result; /* count(4) */
>>> +}
>>> +
>>> +int
>>> +test_switch2 (int i, int j)
>>> +{
>>> + int result = 0; /* count(1) */
>>> + switch (i) /* count(1) */
>>> + {
>>> + case 1:
>>> + result = do_something (2); /* count(#####) */
>>> + break; /* count(#####) */
>>> + case 2:
>>> + result = do_something (1024);
>>> + break;
>>> + case 3:
>>> + case 4:
>>> + if (j == 2) /* count(#####) */
>>> + return do_something (4); /* count(#####) */
>>> + result = do_something (8); /* count(#####) */
>>> + break; /* count(#####) */
>>> + /* We can disable the label itself with the pragma. */
>>> +#pragma GCC suppress_coverage begin
>>> + default: /* count(#) */
>>> + result = do_something (32); /* count(#) */
>>> + switch_m++; /* count(#) */
>>> + break;
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +
>>> + /* We can disable multiple cases with a single pragma. */
>>> +#pragma GCC suppress_coverage begin
>>> + switch (i) /* count(#) */
>>> + {
>>> + case 1: /* count(#) */
>>> + result = do_something (64); /* count(#) */
>>> + break; /* count(#) */
>>> + case 2:
>>> + result = do_something (128); /* count(#) */
>>> + break; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + case 3:
>>> + result = do_something (256); /* count(#####) */
>>> + break; /* count(#####) */
>>> + default:
>>> + result = do_something (512); /* count(1) */
>>> + switch_m++; /* count(1) */
>>> + break;
>>> + }
>>> +
>>> + return result; /* count(1) */
>>> +}
>>> +
>>> +void
>>> +call_switch ()
>>> +{
>>> + switch_val += test_switch (1, 0);
>>> + switch_val += test_switch (3, 0);
>>> + switch_val += test_switch (3, 2);
>>> + switch_val += test_switch (4, 0);
>>> + switch_val += test_switch (16, 0);
>>> + switch_val += switch_m;
>>> + switch_val += test_switch2 (16, 0);
>>> +}
>>> +
>>> +/* The goto tests from gcov-4.c. */
>>> +int goto_val;
>>> +
>>> +int
>>> +test_goto1 (int f)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + if (f) /* count(#) */
>>> + goto lab1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return 1; /* count(1) */
>>> +lab1:
>>> + return 2; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +test_goto2 (int f)
>>> +{
>>> + int i;
>>> + for (i = 0; i < 10; i++) /* count(15) */
>>> + if (i == f) goto lab2; /* count(14) */
>>> + return 4; /* count(1) */
>>> +lab2:
>>> + // Add an empty statement so the attribute is applied to the
>>> return, not the
>>> + // label.
>>> + ;
>>> +#pragma GCC suppress_coverage begin
>>> + return 8; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +}
>>> +
>>> +int
>>> +test_goto3 (int i, int j)
>>> +{
>>> + if (j) /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + goto else_; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> +top:
>>> + if (i) /* count(1) */
>>> + {
>>> + i = do_something (i);
>>> + }
>>> + else
>>> + {
>>> +else_: /* count(1) */
>>> + j = do_something (j); /* count(2) */
>>> +#pragma GCC suppress_coverage begin
>>> + if (j) /* count(#) */
>>> + {
>>> + j = 0; /* count(#) */
>>> + goto top; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + return 16; /* count(1) */
>>> +}
>>> +
>>> +/* Not from gcov-4.c */
>>> +int
>>> +test_goto4 (int f, int g)
>>> +{
>>> + /* The attribute should apply to all statements inside the {},
>>> even the goto
>>> + when the label is inside the suppressed block. When jumping
>>> out, the
>>> + destination should still be counted. */
>>> +#pragma GCC suppress_coverage begin
>>> + {
>>> + if (f) /* count(#) */
>>> + goto inside; /* count(#) */
>>> + if (g) /* count(#) */
>>> + goto outside; /* count(#) */
>>> +
>>> + inside:
>>> + if (g) /* count(#) */
>>> + goto skip; /* count(#) */
>>> + f += 2; /* count(#) */
>>> +
>>> + skip:
>>> + g += 2; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + return 1; /* count(3) */
>>> +outside:
>>> + return 2; /* count(1) */
>>> +}
>>> +
>>> +/* Based on gcov-18.c */
>>> +int
>>> +test_goto5 (int a)
>>> +{
>>> + /* If just one statement is ignored, the whole line should be. */
>>> +#pragma GCC suppress_coverage begin
>>> + noop (); goto baz; lab: a = do_something (a+1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + baz:
>>> + if (a == 1) /* count(2) */
>>> + goto lab; /* count(1) */
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +test_goto6 (int a)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + {
>>> + a += 1; /* count(#) */
>>> + if (a >= 2) /* count(#) */
>>> + goto goto5_1; /* count(#) */
>>> +
>>> + a += 10; /* count(#) */
>>> + if (a >= 20) /* count(#) */
>>> + goto goto5_2; /* count(#) */
>>> +
>>> +
>>> + goto5_1: /* count(#) */
>>> + a *= 3; /* count(#) */
>>> + goto goto5_2; /* count(#) */
>>> +
>>> + goto5_2: /* count(#) */
>>> + a -= 2; /* count(#) */
>>> +
>>> + goto5_3: /* count(#) */
>>> + a += 4; /* count(#) */
>>> + goto goto5_after; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + a *= 2; /* count(-) */
>>> +
>>> + goto5_after: /* count(1) */
>>> + a -= 1; /* count(1) */
>>> + return a; /* count(1) */
>>> +}
>>> +
>>> +void
>>> +call_goto ()
>>> +{
>>> + goto_val += test_goto1 (0);
>>> + goto_val += test_goto1 (1);
>>> + goto_val += test_goto2 (3);
>>> + goto_val += test_goto2 (30);
>>> + goto_val += test_goto3 (0, 1);
>>> +
>>> + goto_val += test_goto4 (0, 0);
>>> + goto_val += test_goto4 (0, 1);
>>> + goto_val += test_goto4 (1, 0);
>>> + goto_val += test_goto4 (1, 1);
>>> +
>>> + goto_val += test_goto5 (1);
>>> + goto_val += test_goto6 (1);
>>> +}
>>> +
>>> +/* Returns, guarded by both plain values and function calls.
>>> +
>>> + The report is slightly different between C and C++ and the #/-
>>> are anchored
>>> + to the right line. */
>>> +void
>>> +return1 (int a, int b, int c)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + if (a) return; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +#pragma GCC suppress_coverage begin
>>> + if (do_something (b)) return; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + if (do_something (c)) /* count(1) */
>>> +#pragma GCC suppress_coverage begin /* count(-) */
>>> + return; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + if (do_something (c)) { /* count(#####) */
>>> +#pragma GCC suppress_coverage begin /* count(-) */
>>> + return; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +}
>>> +
>>> +int
>>> +return2 (int a, int b, int c)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + if (a) return a; /* count(#) */
>>> +#pragma GCC suppress_coverage end /* count(-) */
>>> +#pragma GCC suppress_coverage begin /* count(-) */
>>> + if (do_something (b)) return b; /* count(#) */
>>> +#pragma GCC suppress_coverage end /* count(-) */
>>> + if (do_something (c)) /* count(1) */
>>> +#pragma GCC suppress_coverage begin /* count(-) */
>>> + return c; /* count(#) */
>>> +#pragma GCC suppress_coverage end /* count(-) */
>>> + return 0;
>>> +}
>>> +
>>> +void
>>> +call_return ()
>>> +{
>>> + return1 (1, 0, 0);
>>> + return1 (0, 1, 0);
>>> + return1 (0, 0, 1);
>>> + return2 (1, 0, 0);
>>> + return2 (0, 1, 0);
>>> + return2 (0, 0, 1);
>>> +}
>>> +
>>> +/* From gcov-6.c */
>>> +extern "C" void exit (int);
>>> +int test_exit_val;
>>> +
>>> +void
>>> +test_exit1 (int i)
>>> +{
>>> + /* An abnormal exit should not break suppression. */
>>> +#pragma GCC suppress_coverage begin
>>> + if (i < 0) /* count(#) */
>>> + exit (0); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + test_exit_val += i; /* count(3) */
>>> +}
>>> +
>>> +void
>>> +test_exit2 (int i)
>>> +{
>>> + if (i < 0) /* count(4) */
>>> +#pragma GCC suppress_coverage begin
>>> + exit (0); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + test_exit_val += i; /* count(3) */
>>> +}
>>> +
>>> +void
>>> +test_exit3 (int i)
>>> +{
>>> + /* There can be statements on either side of exit (). */
>>> +#pragma GCC suppress_coverage begin
>>> + if (i < 0) /* count(#) */
>>> + {
>>> + test_exit_val += i; /* count(#) */
>>> + exit (0); /* count(#) */
>>> + test_exit_val += i; /* count(-) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + test_exit_val += i; /* count(3) */
>>> +}
>>> +
>>> +void
>>> +call_exit ()
>>> +{
>>> + for (int i = 0; i != 3; ++i)
>>> + test_exit1 (i);
>>> + for (int i = 0; i != 3; ++i)
>>> + test_exit2 (i);
>>> + for (int i = 0; i != 3; ++i)
>>> + test_exit3 (i);
>>> +
>>> + test_exit2 (-1);
>>> +}
>>> +
>>> +int
>>> +computed_goto1 (int a)
>>> +{
>>> + void *op;
>>> +#pragma GCC suppress_coverage begin
>>> + op = &&dest; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +dest:
>>> + if (op && a > 0) /* count(6) */
>>> + {
>>> + a -= 1; /* count(5) */
>>> + goto *op; /* count(5) */
>>> + }
>>> +
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +computed_goto2 (int a)
>>> +{
>>> + void *op = &&dest; /* count(1) */
>>> +dest:
>>> + ;
>>> +#pragma GCC suppress_coverage begin
>>> + if (op && a > 0) /* count(#) */
>>> + {
>>> + a -= 1; /* count(#) */
>>> + goto *op; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +computed_goto3 (int a)
>>> +{
>>> + void *op = &&dest; /* count(1) */
>>> +dest:
>>> + ;
>>> + if (op && a > 0) /* count(6) */
>>> + {
>>> + a -= 1; /* count(5) */
>>> +#pragma GCC suppress_coverage begin
>>> + goto *op; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +
>>> + return a;
>>> +}
>>> +
>>> +void
>>> +call_computed_goto ()
>>> +{
>>> + computed_goto1 (5);
>>> + computed_goto2 (5);
>>> + computed_goto3 (5);
>>> +}
>>> +
>>> +__attribute__((suppress_coverage)) int
>>> +suppressed_function (int a)
>>> +{
>>> + int c;
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + return c; /* count(#) */
>>> +}
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> +int
>>> +pragma_begin_outside (int a)
>>> +{
>>> + int c;
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + return c; /* count(#) */
>>> +}
>>> +#pragma GCC suppress_coverage end
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> +int
>>> +pragma_end_middle (int a)
>>> +{
>>> + int c;
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + int d = a - 1; /* count(1) */
>>> + c = a+b+d; /* count(1) */
>>> + return c; /* count(1) */
>>> +}
>>> +
>>> +int main ()
>>> +{
>>> + empty_body_for_loop ();
>>> + ignored_for_loop ();
>>> + declarations (1);
>>> + compound_statements (1);
>>> + call_while ();
>>> + call_for ();
>>> + call_ifelse ();
>>> + call_switch ();
>>> + call_goto ();
>>> + call_return ();
>>> + call_computed_goto ();
>>> + suppressed_function (1);
>>> + pragma_begin_outside (1);
>>> + pragma_end_middle (1);
>>> +
>>> + /* The final test will actually exit, so make sure to call it
>>> last. */
>>> + call_exit ();
>>> + return 0;
>>> +}
>>> +
>>> +/* { dg-final { run-gcov { gcov-24.C } } } */
>>> diff --git a/gcc/testsuite/g++.dg/gcov/gcov-25.C b/gcc/testsuite/g+
>>> +.dg/gcov/gcov-25.C
>>> new file mode 100644
>>> index 000000000000..cebf27d5e596
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/gcov/gcov-25.C
>>> @@ -0,0 +1,23 @@
>>> +/* { dg-options "--coverage" } */
>>> +/* { dg-do run } */
>>> +
>>> +/* [[attribute]] syntax instead of __attribute__((attr)). */
>>> +
>>> +[[gnu::suppress_coverage]] int
>>> +suppressed_function (int a)
>>> +{
>>> + int c;
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + return c; /* count(#) */
>>> +}
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + suppressed_function (1);
>>> +}
>>> +
>>> +/* { dg-final { run-gcov gcov-25.C } } */
>>> diff --git a/gcc/testsuite/g++.dg/gcov/gcov-26.C b/gcc/testsuite/g+
>>> +.dg/gcov/gcov-26.C
>>> new file mode 100644
>>> index 000000000000..60be9a38c18a
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/gcov/gcov-26.C
>>> @@ -0,0 +1,548 @@
>>> +/* A collection of C++ specific constructs with coverage disabled. */
>>> +
>>> +/* { dg-options "--coverage" } */
>>> +/* { dg-do run } */
>>> +
>>> +#include <stdexcept>
>>> +
>>> +void noop () {}
>>> +void noop (int) {}
>>> +
>>> +void
>>> +throws (int i)
>>> +{
>>> + if (i)
>>> + throw 1;
>>> +}
>>> +
>>> +void
>>> +throws_stdexcept (int i)
>>> +{
>>> + switch (i)
>>> + {
>>> + case 1: throw std::length_error("length error");
>>> + case 2: throw std::domain_error("domain error");
>>> + case 3: throw std::runtime_error("runtime error");
>>> + default: return;
>>> + }
>>> +}
>>> +
>>> +
>>> +/* We can disable coverage for statements inside try/catch blocks. */
>>> +void
>>> +try_catch1 ()
>>> +{
>>> + try
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + throws (0); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + throws (0); /* count(1) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count(=====) */
>>> + }
>>> +
>>> + try
>>> + {
>>> + throws (1); /* count(1) */
>>> + throws (0); /* count(#####) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count (1) */
>>> + }
>>> +}
>>> +
>>> +void
>>> +try_catch2 ()
>>> +{
>>> + try
>>> + {
>>> + throws (0); /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + throws (0); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count(=====) */
>>> + }
>>> +
>>> + try
>>> + {
>>> + throws (1); /* count(1) */
>>> + throws (0); /* count(#####) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count (1) */
>>> + }
>>> +}
>>> +
>>> +void
>>> +try_catch3 ()
>>> +{
>>> + try
>>> + {
>>> + throws (0); /* count(1) */
>>> + throws (0); /* count(1) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count(=====) */
>>> + }
>>> +
>>> + try
>>> + {
>>> + throws (1); /* count(1) */
>>> + throws (0); /* count(#####) */
>>> + }
>>> + catch (...)
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + noop (); /* count (#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +}
>>> +
>>> +/* We can disable try-catch altogether. */
>>> +void
>>> +try_catch4 ()
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + try
>>> + {
>>> + throws (0); /* count(#) */
>>> + throws (0); /* count(#) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + try
>>> + {
>>> + throws (1); /* count(1) */
>>> + throws (0); /* count(#####) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count (#) */
>>> + }
>>> +}
>>> +
>>> +void
>>> +try_catch5 ()
>>> +{
>>> + try
>>> + {
>>> + throws (0); /* count(1) */
>>> + throws (0); /* count(1) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count(=====) */
>>> + }
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + try
>>> + {
>>> + throws (1); /* count(#) */
>>> + throws (0); /* count(#) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count (#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +}
>>> +
>>> +void
>>> +try_catch6 ()
>>> +{
>>> + try
>>> + {
>>> + throws (0); /* count(1) */
>>> + throws (0); /* count(1) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count(=====) */
>>> + }
>>> +
>>> + try
>>> + {
>>> + throws (1); /* count(1) */
>>> + throws (0); /* count(#####) */
>>> + }
>>> + catch (...)
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + {
>>> + noop (); /* count (#) */
>>> + noop (); /* count (#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +}
>>> +
>>> +void
>>> +try_catch7 ()
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + try
>>> + {
>>> + throws (0); /* count(#) */
>>> + throws (0); /* count(#) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +}
>>> +
>>> +void
>>> +try_catch8 ()
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + try
>>> + {
>>> + throws_stdexcept (0); /* count(#) */
>>> + throws_stdexcept (1); /* count(#) */
>>> + }
>>> + catch (std::length_error&)
>>> + {
>>> + noop (1); /* count(#) */
>>> + }
>>> + catch (std::domain_error&)
>>> + {
>>> + noop (2); /* count(#) */
>>> + }
>>> + catch (std::runtime_error&)
>>> + {
>>> + noop (3); /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + try
>>> + {
>>> + throws_stdexcept (1); /* count(1) */
>>> + throws_stdexcept (0); /* count(#####) */
>>> + }
>>> + catch (std::length_error&)
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + noop (4); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + catch (std::domain_error&)
>>> + {
>>> + noop (5); /* count(=====) */
>>> + }
>>> + catch (std::runtime_error&)
>>> + {
>>> + noop (6); /* count(=====) */
>>> + }
>>> +
>>> + try
>>> + {
>>> + throws_stdexcept (2); /* count(1) */
>>> + throws_stdexcept (0); /* count(#####) */
>>> + }
>>> + catch (std::length_error&)
>>> + {
>>> + noop (7); /* count(=====) */
>>> + }
>>> + catch (std::domain_error&)
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + noop (8); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + catch (std::runtime_error&)
>>> + {
>>> + noop (9); /* count(=====) */
>>> + }
>>> +}
>>> +
>>> +/* We can start/stop coverage in different try/catch blocks, even
>>> across
>>> + different expressions. */
>>> +void
>>> +try_catch9 ()
>>> +{
>>> + try
>>> + {
>>> + throws (0); /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + throws (0); /* count(#) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count(#) */
>>> + }
>>> +
>>> + try
>>> + {
>>> + throws (1); /* count(#) */
>>> + throws (0); /* count(#) */
>>> + }
>>> + catch (...)
>>> + {
>>> + noop (); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + noop (); /* count(1) */
>>> + }
>>> +}
>>> +
>>> +/* Throws are disabled, either directly or through its surrounding
>>> block. */
>>> +int
>>> +ifelse_throw1 (int f)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + if (f >= 2) /* count(#) */
>>> + throw 1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return f; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +ifelse_throw2 (int f)
>>> +{
>>> + if (f >= 2) /* count(2) */
>>> +#pragma GCC suppress_coverage begin
>>> + throw 1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return f; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +ifelse_throw3 (int f)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + if (f >= 2) /* count(#) */
>>> + throw 1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return f; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +ifelse_throw4 (int f)
>>> +{
>>> + if (f >= 2) /* count(2) */
>>> +#pragma GCC suppress_coverage begin
>>> + throw 1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return f; /* count(1) */
>>> +}
>>> +
>>> +int ctor_x;
>>> +/* Disabling coverage for a default constructor/initialization. */
>>> +void
>>> +ctor1 ()
>>> +{
>>> + class C
>>> + {
>>> + int v;
>>> + public:
>>> + C() : v(5) {}
>>> + };
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + C c; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + // arbitrary action between ctor+dtor
>>> + ctor_x = 1; /* count(1) */
>>> +}
>>> +
>>> +/* Disabling coverage for a constructor/initialization with args. */
>>> +void
>>> +ctor2 (int a)
>>> +{
>>> + class C
>>> + {
>>> + public:
>>> + explicit C (int e) : v (e) {}
>>> + int v;
>>> + };
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + C c (a); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + // arbitrary action between ctor+dtor
>>> + ctor_x = 1; /* count(1) */
>>> +}
>>> +
>>> +void
>>> +ctor3 ()
>>> +{
>>> + class C
>>> + {
>>> + int v;
>>> + public:
>>> + C() : v(5) {}
>>> + };
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + C c; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + ctor_x = 1; /* count(1) */
>>> +}
>>> +
>>> +/* Disabling coverage for a constructor/initialization with args. */
>>> +void
>>> +ctor4 (int a)
>>> +{
>>> + class C
>>> + {
>>> + public:
>>> + explicit C (int e) : v (e) {}
>>> + int v;
>>> + };
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + C c (a); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + ctor_x = 1; /* count(1) */
>>> +}
>>> +
>>> +
>>> +template <typename T>
>>> +T
>>> +incr1 (T v)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + v += 1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return v;
>>> +}
>>> +
>>> +template <typename T>
>>> +T
>>> +incr2 (T v)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + v += 2; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return v;
>>> +}
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> +template <typename T>
>>> +T
>>> +incr3 (T v)
>>> +{
>>> + v += 3; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return v;
>>> +}
>>> +
>>> +template <typename T>
>>> +__attribute__((suppress_coverage))
>>> +T
>>> +decr1 (T v)
>>> +{
>>> + v += 1; /* count(#) */
>>> + return v; /* count(#) */
>>> +}
>>> +
>>> +template <typename T>
>>> +[[gnu::suppress_coverage]]
>>> +T
>>> +decr2 (T v)
>>> +{
>>> + v += 1; /* count(#) */
>>> + return v; /* count(#) */
>>> +}
>>> +
>>> +template <typename T>
>>> +__attribute__((suppress_coverage))
>>> +void
>>> +templated_function_level_class (T a)
>>> +{
>>> + class C
>>> + {
>>> + public:
>>> + /* Function-level classes would still be counted. */
>>> + explicit C (T e) : v (e) {}
>>> + T v;
>>> + };
>>> +
>>> + C c (a); /* count(#) */
>>> + ctor_x = 1; /* count(#) */
>>> +}
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> +template <typename T>
>>> +void
>>> +templated_function_level_class_pragma (T a)
>>> +{
>>> + class C
>>> + {
>>> + public:
>>> + /* Function-level will now be suppressed. */
>>> + explicit C (T e) : v (e) {} /* count (#) */
>>> + T v;
>>> + };
>>> +
>>> + C c (a); /* count(#) */
>>> + ctor_x = 1; /* count(#) */
>>> +}
>>> +#pragma GCC suppress_coverage end
>>> +
>>> +int main ()
>>> +{
>>> + try_catch1 ();
>>> + try_catch2 ();
>>> + try_catch3 ();
>>> + try_catch4 ();
>>> + try_catch5 ();
>>> + try_catch6 ();
>>> + try_catch7 ();
>>> + try_catch8 ();
>>> + try_catch9 ();
>>> +
>>> + try { ifelse_throw1 (1); } catch (...) {}
>>> + try { ifelse_throw1 (2); } catch (...) {}
>>> +
>>> + try { ifelse_throw2 (1); } catch (...) {}
>>> + try { ifelse_throw2 (2); } catch (...) {}
>>> +
>>> + try { ifelse_throw3 (1); } catch (...) {}
>>> + try { ifelse_throw3 (2); } catch (...) {}
>>> +
>>> + try { ifelse_throw4 (1); } catch (...) {}
>>> + try { ifelse_throw4 (2); } catch (...) {}
>>> +
>>> + ctor1 ();
>>> + ctor2 (5);
>>> + ctor3 ();
>>> + ctor4 (5);
>>> +
>>> + incr1 <int> (1);
>>> + incr1 <double> (2.0);
>>> + incr2 <int> (1);
>>> + incr2 <double> (2.0);
>>> + incr3 <int> (1);
>>> + incr3 <double> (3.0);
>>> +
>>> + decr1 <int> (1);
>>> + decr1 <double> (2.0);
>>> + decr2 <int> (1);
>>> + decr2 <double> (2.0);
>>> +
>>> + templated_function_level_class <int> (5);
>>> + templated_function_level_class <double> (5.0);
>>> +
>>> + templated_function_level_class_pragma <int> (5);
>>> + templated_function_level_class_pragma <double> (5.0);
>>> +}
>>> +
>>> +/* { dg-final { run-gcov gcov-26.C } } */
>>> diff --git a/gcc/testsuite/g++.dg/gcov/gcov-27.C b/gcc/testsuite/g+
>>> +.dg/gcov/gcov-27.C
>>> new file mode 100644
>>> index 000000000000..2c96892e7efb
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/gcov/gcov-27.C
>>> @@ -0,0 +1,80 @@
>>> +/* { dg-require-effective-target c++11 } */
>>> +/* { dg-options "--coverage -std=c++11" } */
>>> +/* { dg-do run } */
>>> +
>>> +void lambda1 ()
>>> +{
>>> + /* From pr86109.C */
>>> + auto partially_uncovered_lambda1 = [](int i) { /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + if (i > 10) /* count(#) */
>>> + return 0; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return 1; /* count(#####) */
>>> + };
>>> +
>>> + auto partially_uncovered_lambda2 = [](int i) { /* count(1) */
>>> + if (i > 10) /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + return 0; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return 1; /* count(#####) */
>>> + };
>>> +
>>> + partially_uncovered_lambda1 (20);
>>> + partially_uncovered_lambda2 (20);
>>> +}
>>> +
>>> +void lambda2 ()
>>> +{
>>> + /* From pr86109.C */
>>> +#pragma GCC suppress_coverage begin
>>> + auto partially_uncovered_lambda1 = [](int i) { /* count(#) */
>>> + if (i > 10) /* count(#) */
>>> + return 0; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return 1; /* count(#####) */
>>> + };
>>> +
>>> + auto partially_uncovered_lambda2 = [](int i) { /* count(1) */
>>> + if (i > 10) /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + return 0; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return 1; /* count(#####) */
>>> + };
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + auto partially_uncovered_lambda3 = [](int i) { /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + if (i > 10) /* count(1) */
>>> + return 0; /* count(1) */
>>> + return 1; /* count(#####) */
>>> + };
>>> +
>>> + partially_uncovered_lambda1 (20);
>>> + partially_uncovered_lambda2 (20);
>>> + partially_uncovered_lambda3 (20);
>>> +}
>>> +
>>> +void lambda3 ()
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + auto fully_covered_lambda1 = [](int i) { /* count(#) */
>>> + if (i > 10) /* count(#) */
>>> + return 0; /* count(#) */
>>> + return 1; /* count(#) */
>>> + };
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + fully_covered_lambda1 (20);
>>> +}
>>> +
>>> +int main ()
>>> +{
>>> + lambda1 ();
>>> + lambda2 ();
>>> + lambda3 ();
>>> +}
>>> +
>>> +/* { dg-final { run-gcov gcov-27.C } } */
>>> diff --git a/gcc/testsuite/gcc.dg/pragma-suppress-coverage.c b/gcc/
>>> testsuite/gcc.dg/pragma-suppress-coverage.c
>>> new file mode 100644
>>> index 000000000000..2a3bc42e8831
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/pragma-suppress-coverage.c
>>> @@ -0,0 +1,32 @@
>>> +/* Verify that we use emit diagnostics for #pragma GCC
>>> suppress_coverage. */
>>> +
>>> +/* { dg-do assemble } */
>>> +/* { dg-options "-fdiagnostics-show-caret" } */
>>> +
>>> +#pragma GCC suppress_coverage end
>>> +/* { dg-warning "no matching begin for '#pragma GCC
>>> suppress_coverage end'" "" { target *-*-* } .-1 }
>>> + { dg-begin-multiline-output "" }
>>> + #pragma GCC suppress_coverage end
>>> + ^~~
>>> + { dg-end-multiline-output "" } */
>>> +
>>> +#pragma GCC suppress_coverage
>>> +/* { dg-warning "'#pragma GCC suppress_coverage' must be followed by
>>> 'begin' or 'end'" "" { target *-*-* } .-1 }
>>> + { dg-begin-multiline-output "" }
>>> + #pragma GCC suppress_coverage
>>> + ^~~
>>> + { dg-end-multiline-output "" } */
>>> +
>>> +#pragma GCC suppress_coverage begin more
>>> +/* { dg-warning "junk at end of '#pragma GCC suppress_coverage'"
>>> "" { target *-*-* } .-1 }
>>> + { dg-begin-multiline-output "" }
>>> + #pragma GCC suppress_coverage begin more
>>> + ^~~~
>>> + { dg-end-multiline-output "" } */
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> +/* { dg-warning "'#pragma GCC suppress_coverage begin' was already
>>> in effect, ignored" "" { target *-*-* } .-1 }
>>> + { dg-begin-multiline-output "" }
>>> + #pragma GCC suppress_coverage begin
>>> + ^~~
>>> + { dg-end-multiline-output "" } */
>>> diff --git a/gcc/testsuite/gcc.misc-tests/gcov-35.c b/gcc/testsuite/
>>> gcc.misc-tests/gcov-35.c
>>> new file mode 100644
>>> index 000000000000..f141fdf98510
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.misc-tests/gcov-35.c
>>> @@ -0,0 +1,1061 @@
>>> +/* { dg-options "--coverage" } */
>>> +/* { dg-do run } */
>>> +
>>> +extern void exit (int);
>>> +
>>> +void noop () {}
>>> +
>>> +int do_something (int i) { return i; }
>>> +
>>> +/* Empty loop bodies should still suppress coverage for the for (;;)
>>> and compile
>>> + fine. */
>>> +int
>>> +empty_body_for_loop ()
>>> +{
>>> + int i;
>>> +#pragma GCC suppress_coverage begin
>>> + for (i = 0; i < 10; i++) /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + ;
>>> + return i;
>>> +}
>>> +
>>> +/* Suppressing the loop should suppress everything within the loop,
>>> but not the
>>> + return. */
>>> +int
>>> +ignored_for_loop ()
>>> +{
>>> + int i;
>>> +#pragma GCC suppress_coverage begin
>>> + for (i = 0; i < 10; i++) /* count(#) */
>>> + {
>>> + noop (); /* count(#) */
>>> + noop (); /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + /* Making the for (;;) multi line should report count per line. */
>>> +#pragma GCC suppress_coverage begin
>>> + for (i = 0; /* count(#) */
>>> + i < 20; /* count(#) */
>>> + i++) /* count(#) */
>>> + {
>>> + noop (); /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + noop ();
>>> + i++;
>>> +
>>> + return 0; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +declarations (int a)
>>> +{
>>> + // Should work when it is the first declaration
>>> +#pragma GCC suppress_coverage begin
>>> + int b = a + 1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + // declarations with no init has no, and suppress_coverage is a no-op
>>> +#pragma GCC suppress_coverage begin
>>> + int c; /* count(-) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + a *= 2; /* count(1) */
>>> +
>>> + // Should work when it is not the first declaration
>>> +#pragma GCC suppress_coverage begin
>>> + int d = a - 1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + c = a+b+d; /* count(1) */
>>> + return c;
>>> +}
>>> +
>>> +int
>>> +compound_statements (int a)
>>> +{
>>> + int c; /* count(-) */
>>> +#pragma GCC suppress_coverage begin
>>> + {
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + return c; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +while1 (int a)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + while (a > 0) /* count(#) */
>>> + a = do_something (a - 1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return a;
>>> +}
>>> +
>>> +/* If the pragma stops blocks from merging this would end up with
>>> the wrong
>>> + count. The while (cond) should run 6 times (a = 5), but earlier
>>> drafts
>>> + clocked it at 7 because an empty block at the end of the body
>>> would have
>>> + coverage suppressed, while the loop header did which prevented
>>> merging. */
>>> +int
>>> +while2 (int a)
>>> +{
>>> + while (a > 0) /* count(6) */
>>> +#pragma GCC suppress_coverage begin
>>> + a = do_something (a - 1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +dowhile1 (int a)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + do
>>> + {
>>> + a = do_something (a - 1); /* count(#) */
>>> + } while (a > 0); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +dowhile2 (int a)
>>> +{
>>> + do
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + a = do_something (a - 1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + } while (a > 0); /* count(5) */
>>> + return a;
>>> +}
>>> +
>>> +void
>>> +call_while ()
>>> +{
>>> + while1 (5);
>>> + while2 (5);
>>> + dowhile1 (5);
>>> + dowhile2 (5);
>>> +}
>>> +
>>> +/* Based on gcov-pr85217.c, a loop with both breaks and continues. */
>>> +int
>>> +for1 ()
>>> +{
>>> + int a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(2) */
>>> + {
>>> + break; /* count(1) */
>>> + a; /* count(-) */
>>> + continue; /* count(1) */
>>> + }
>>> + continue; /* count(1) */
>>> + }
>>> +
>>> + a = 0;
>>> +#pragma GCC suppress_coverage begin
>>> + for (;; a++) /* count(#) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(#) */
>>> + {
>>> + break; /* count(#) */
>>> + a; /* count(-) */
>>> + continue; /* count(#) */
>>> + }
>>> + continue; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> +#pragma GCC suppress_coverage begin
>>> + if (a) /* count(#) */
>>> + {
>>> + break; /* count(#) */
>>> + a; /* count(-) */
>>> + continue; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + continue; /* count(1) */
>>> + }
>>> +
>>> + a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(2) */
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + break; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + a; /* count(-) */
>>> + continue; /* count(1) */
>>> + }
>>> + continue; /* count(1) */
>>> + }
>>> +
>>> + a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(2) */
>>> + {
>>> + break; /* count(1) */
>>> + a; /* count(-) */
>>> +#pragma GCC suppress_coverage begin
>>> + continue; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + continue; /* count(1) */
>>> + }
>>> +
>>> + a = 0;
>>> + for (;; a++) /* count(1) */
>>> + {
>>> + int c[1];
>>> + if (a) /* count(2) */
>>> + {
>>> + break; /* count(1) */
>>> + a; /* count(-) */
>>> + continue; /* count(1) */
>>> + }
>>> +#pragma GCC suppress_coverage begin
>>> + continue; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +
>>> + return a;
>>> +}
>>> +
>>> +/* A loop with break. */
>>> +int
>>> +for2 (int n)
>>> +{
>>> + int acc = 0;
>>> + for (int i = 0; i < n; ++i) /* count(7) */
>>> + {
>>> + acc += do_something (i); /* count(7) */
>>> +#pragma GCC suppress_coverage begin
>>> + if (acc > 10) /* count(#) */
>>> + break; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + acc -= 1; /* count(6) */
>>> + }
>>> + return acc;
>>> +}
>>> +
>>> +int
>>> +for3 (int n)
>>> +{
>>> + int acc = 0;
>>> + for (int i = 0; i < n; ++i) /* count(7) */
>>> + {
>>> + acc += do_something (i); /* count(7) */
>>> + if (acc > 10) /* count(7) */
>>> + /* The block/line gets anchored to the attribute, not the break,
>>> but
>>> + the data is fine. */
>>> +#pragma GCC suppress_coverage begin
>>> + break; /* count(-) */
>>> +#pragma GCC suppress_coverage end
>>> + acc -= 1; /* count(6) */
>>> + }
>>> + return acc;
>>> +}
>>> +
>>> +/* Based on the test in gcov-4.c */
>>> +int for_val1;
>>> +int for_temp;
>>> +int
>>> +nested_for1 (int m, int n, int o)
>>> +{
>>> + int i, j, k;
>>> + for_temp = 1; /* count(6) */
>>> + for (i = 0; i < n; i++) /* count(20) */
>>> + for (j = 0; j < m; j++) /* count(44) */
>>> +#pragma GCC suppress_coverage begin
>>> + for (k = 0; k < o; k++) /* count(#) */
>>> + for_temp++; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return for_temp; /* count(6) */
>>> +}
>>> +
>>> +void
>>> +call_for ()
>>> +{
>>> + for1 ();
>>> + for2 (10);
>>> + for3 (10);
>>> +
>>> + for_val1 += nested_for1 (0, 0, 0);
>>> + for_val1 += nested_for1 (1, 0, 0);
>>> + for_val1 += nested_for1 (1, 3, 0);
>>> + for_val1 += nested_for1 (1, 3, 1);
>>> + for_val1 += nested_for1 (3, 1, 5);
>>> + for_val1 += nested_for1 (3, 7, 3);
>>> +}
>>> +
>>> +int ifelse_val1;
>>> +int ifelse_val2;
>>> +int ifelse_val3;
>>> +
>>> +int
>>> +test_ifelse1 (int i, int j)
>>> +{
>>> + int result = 0;
>>> + /* We can ignore the THEN. */
>>> + if (i) /* count(5) */
>>> + if (j) /* count(3) */
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (4); /* count(#) */
>>> + else
>>> +#pragma GCC suppress_coverage end
>>> + result = do_something (1024);
>>> + /* We can ignore the ELSE. */
>>> + else
>>> + if (j) /* count(2) */
>>> + result = do_something (1); /* count(1) */
>>> + else
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (2); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + if (i > j) /* count(5) */
>>> + result = do_something (result*2); /* count(1) */
>>> +
>>> + /* We can ignore the whole if-then-else. */
>>> + if (i > 10) /* count(5) */
>>> +#pragma GCC suppress_coverage begin
>>> + if (j > 10) /* count(#) */
>>> + result = do_something (result*4); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return result; /* count(5) */
>>> +}
>>> +
>>> +int
>>> +test_ifelse2 (int i)
>>> +{
>>> + int result = 0;
>>> +#pragma GCC suppress_coverage begin
>>> + if (!i) /* count(#) */
>>> + result = do_something (1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + if (i == 1) /* count(6) */
>>> + result = do_something (1024);
>>> +
>>> + if (i == 2) /* count(6) */
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (2); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + if (i == 3) /* count(6) */
>>> +#pragma GCC suppress_coverage begin
>>> + return do_something (8); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + if (i == 4) /* count(#) */
>>> + return do_something (2048); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return result; /* count(4) */
>>> +}
>>> +
>>> +int
>>> +test_ifelse3 (int i, int j)
>>> +{
>>> + int result = 1;
>>> + /* Multi-condition ifs are suppressed, too */
>>> +#pragma GCC suppress_coverage begin
>>> + if (i > 10 && j > i && j < 20) /* count(#) */
>>> + result = do_something (16); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + if (i == 3 || j == 47 || i == j) /* count(11) */
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (64); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return result; /* count(11) */
>>> +}
>>> +
>>> +/* These are based on gcov-17.c */
>>> +int
>>> +test_ifelse4 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> +#pragma GCC suppress_coverage begin
>>> + if (true_var) /* count(#) */
>>> + {
>>> + if (false_var) /* count(#) */
>>> + ret = 111; /* count(#) */
>>> + }
>>> + else
>>> + ret = 999; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse5 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + if (true_var) /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + {
>>> + if (false_var) /* count(#) */
>>> + ret = 111; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + else
>>> + ret = 999; /* count(#####) */
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse6 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + if (true_var) /* count(1) */
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + if (false_var) /* count(#) */
>>> + ret = 111; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + else
>>> + ret = 999; /* count(#####) */
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse7 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + if (true_var) /* count(1) */
>>> + {
>>> + if (false_var) /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + ret = 111; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + else
>>> + ret = 999; /* count(#####) */
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse8 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + if (true_var) /* count(1) */
>>> + {
>>> + if (false_var) /* count(1) */
>>> + ret = 111; /* count(#####) */
>>> + }
>>> + else
>>> +#pragma GCC suppress_coverage begin
>>> + ret = 999; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return ret;
>>> +}
>>> +
>>> +int
>>> +test_ifelse9 (int true_var, int false_var)
>>> +{
>>> + unsigned int ret = 0;
>>> + /* With the pragma we can suppress condition, but still observe
>>> the then-else. */
>>> +#pragma GCC suppress_coverage begin
>>> + if (true_var) /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + {
>>> + if (false_var) /* count(1) */
>>> + ret = 111; /* count(#####) */
>>> + }
>>> + else
>>> + ret = 999; /* count(#####) */
>>> + return ret;
>>> +}
>>> +
>>> +void
>>> +call_ifelse ()
>>> +{
>>> + ifelse_val1 += test_ifelse1 (0, 2);
>>> + ifelse_val1 += test_ifelse1 (0, 0);
>>> + ifelse_val1 += test_ifelse1 (1, 2);
>>> + ifelse_val1 += test_ifelse1 (10, 2);
>>> + ifelse_val1 += test_ifelse1 (11, 11);
>>> +
>>> + ifelse_val2 += test_ifelse2 (0);
>>> + ifelse_val2 += test_ifelse2 (2);
>>> + ifelse_val2 += test_ifelse2 (2);
>>> + ifelse_val2 += test_ifelse2 (2);
>>> + ifelse_val2 += test_ifelse2 (3);
>>> + ifelse_val2 += test_ifelse2 (3);
>>> +
>>> + ifelse_val3 += test_ifelse3 (11, 19);
>>> + ifelse_val3 += test_ifelse3 (25, 27);
>>> + ifelse_val3 += test_ifelse3 (11, 22);
>>> + ifelse_val3 += test_ifelse3 (11, 10);
>>> + ifelse_val3 += test_ifelse3 (21, 32);
>>> + ifelse_val3 += test_ifelse3 (21, 20);
>>> + ifelse_val3 += test_ifelse3 (1, 2);
>>> + ifelse_val3 += test_ifelse3 (32, 31);
>>> + ifelse_val3 += test_ifelse3 (3, 0);
>>> + ifelse_val3 += test_ifelse3 (0, 47);
>>> + ifelse_val3 += test_ifelse3 (65, 65);
>>> +
>>> + test_ifelse4 (1, 0);
>>> + test_ifelse5 (1, 0);
>>> + test_ifelse6 (1, 0);
>>> + test_ifelse7 (1, 0);
>>> + test_ifelse8 (1, 0);
>>> + test_ifelse9 (1, 0);
>>> +}
>>> +
>>> +int switch_val, switch_m;
>>> +int
>>> +test_switch (int i, int j)
>>> +{
>>> + int result = 0; /* count(5) */
>>> + /* We can suppress individual statements and breaks in the
>>> switch. */
>>> + switch (i) /* count(5) */
>>> + {
>>> + case 1:
>>> +#pragma GCC suppress_coverage begin
>>> + result = do_something (2); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + break; /* count(1) */
>>> + case 2:
>>> + result = do_something (1024);
>>> + break;
>>> + case 3:
>>> + case 4:
>>> + if (j == 2) /* count(3) */
>>> + return do_something (4); /* count(1) */
>>> + result = do_something (8); /* count(2) */
>>> +#pragma GCC suppress_coverage begin
>>> + break; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + default:
>>> + result = do_something (32); /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + switch_m++; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + break;
>>> + }
>>> +
>>> + /* We can suppress the whole switch. */
>>> +#pragma GCC suppress_coverage begin
>>> + switch (i)
>>> + {
>>> + case 1:
>>> + result = do_something (64); /* count(#) */
>>> + break; /* count(#) */
>>> + case 2:
>>> + result = do_something (128); /* count(#) */
>>> + break; /* count(#) */
>>> + case 3:
>>> + result = do_something (256); /* count(#) */
>>> + break; /* count(#) */
>>> + default:
>>> + result = do_something (512); /* count(#) */
>>> + switch_m++; /* count(#) */
>>> + break;
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return result; /* count(4) */
>>> +}
>>> +
>>> +int
>>> +test_switch2 (int i, int j)
>>> +{
>>> + int result = 0; /* count(1) */
>>> + switch (i) /* count(1) */
>>> + {
>>> + case 1:
>>> + result = do_something (2); /* count(#####) */
>>> + break; /* count(#####) */
>>> + case 2:
>>> + result = do_something (1024);
>>> + break;
>>> + case 3:
>>> + case 4:
>>> + if (j == 2) /* count(#####) */
>>> + return do_something (4); /* count(#####) */
>>> + result = do_something (8); /* count(#####) */
>>> + break; /* count(#####) */
>>> + /* We can suppress the label itself with the pragma. */
>>> +#pragma GCC suppress_coverage begin
>>> + default: /* count(#) */
>>> + result = do_something (32); /* count(#) */
>>> + switch_m++; /* count(#) */
>>> + break;
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +
>>> + /* We can suppress multiple cases with a single pragma. */
>>> +#pragma GCC suppress_coverage begin
>>> + switch (i) /* count(#) */
>>> + {
>>> + case 1: /* count(#) */
>>> + result = do_something (64); /* count(#) */
>>> + break; /* count(#) */
>>> + case 2:
>>> + result = do_something (128); /* count(#) */
>>> + break; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + case 3:
>>> + result = do_something (256); /* count(#####) */
>>> + break; /* count(#####) */
>>> + default:
>>> + result = do_something (512); /* count(1) */
>>> + switch_m++; /* count(1) */
>>> + break;
>>> + }
>>> +
>>> + return result; /* count(1) */
>>> +}
>>> +
>>> +void
>>> +call_switch ()
>>> +{
>>> + switch_val += test_switch (1, 0);
>>> + switch_val += test_switch (3, 0);
>>> + switch_val += test_switch (3, 2);
>>> + switch_val += test_switch (4, 0);
>>> + switch_val += test_switch (16, 0);
>>> + switch_val += switch_m;
>>> + switch_val += test_switch2 (16, 0);
>>> +}
>>> +
>>> +/* The goto tests from gcov-4.c. */
>>> +int goto_val;
>>> +
>>> +int
>>> +test_goto1 (int f)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + if (f) /* count(#) */
>>> + goto lab1; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + return 1; /* count(1) */
>>> +lab1:
>>> + return 2; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +test_goto2 (int f)
>>> +{
>>> + int i;
>>> + for (i = 0; i < 10; i++) /* count(15) */
>>> + if (i == f) goto lab2; /* count(14) */
>>> + return 4; /* count(1) */
>>> +lab2:
>>> + // Add an empty statement so the attribute is applied to the
>>> return, not the
>>> + // label.
>>> + ;
>>> +#pragma GCC suppress_coverage begin
>>> + return 8; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +}
>>> +
>>> +int
>>> +test_goto3 (int i, int j)
>>> +{
>>> + if (j) /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + goto else_; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +
>>> +top:
>>> + if (i) /* count(1) */
>>> + {
>>> + i = do_something (i);
>>> + }
>>> + else
>>> + {
>>> +else_: /* count(1) */
>>> + j = do_something (j); /* count(2) */
>>> +#pragma GCC suppress_coverage begin
>>> + if (j) /* count(#) */
>>> + {
>>> + j = 0; /* count(#) */
>>> + goto top; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + return 16; /* count(1) */
>>> +}
>>> +
>>> +/* Not from gcov-4.c */
>>> +int
>>> +test_goto4 (int f, int g)
>>> +{
>>> + /* The attribute should apply to all statements inside the {},
>>> even the goto
>>> + when the label is inside the suppressed block. When jumping
>>> out, the
>>> + destination should still be counted. */
>>> +#pragma GCC suppress_coverage begin
>>> + {
>>> + if (f) /* count(#) */
>>> + goto inside; /* count(#) */
>>> + if (g) /* count(#) */
>>> + goto outside; /* count(#) */
>>> +
>>> + inside:
>>> + if (g) /* count(#) */
>>> + goto skip; /* count(#) */
>>> + f += 2; /* count(#) */
>>> +
>>> + skip:
>>> + g += 2; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + return 1; /* count(3) */
>>> +outside:
>>> + return 2; /* count(1) */
>>> +}
>>> +
>>> +/* Based on gcov-18.c */
>>> +int
>>> +test_goto5 (int a)
>>> +{
>>> + /* If just one statement is ignored, the whole line should be. */
>>> +#pragma GCC suppress_coverage begin
>>> + noop (); goto baz; lab: a = do_something (a+1); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + baz:
>>> + if (a == 1) /* count(2) */
>>> + goto lab; /* count(1) */
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +test_goto6 (int a)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + {
>>> + a += 1; /* count(#) */
>>> + if (a >= 2) /* count(#) */
>>> + goto goto5_1; /* count(#) */
>>> +
>>> + a += 10; /* count(#) */
>>> + if (a >= 20) /* count(#) */
>>> + goto goto5_2; /* count(#) */
>>> +
>>> +
>>> + goto5_1: /* count(#) */
>>> + a *= 3; /* count(#) */
>>> + goto goto5_2; /* count(#) */
>>> +
>>> + goto5_2: /* count(#) */
>>> + a -= 2; /* count(#) */
>>> +
>>> + goto5_3: /* count(#) */
>>> + a += 4; /* count(#) */
>>> + goto goto5_after; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + a *= 2; /* count(-) */
>>> +
>>> + goto5_after: /* count(1) */
>>> + a -= 1; /* count(1) */
>>> + return a; /* count(1) */
>>> +}
>>> +
>>> +void
>>> +call_goto ()
>>> +{
>>> + goto_val += test_goto1 (0);
>>> + goto_val += test_goto1 (1);
>>> + goto_val += test_goto2 (3);
>>> + goto_val += test_goto2 (30);
>>> + goto_val += test_goto3 (0, 1);
>>> +
>>> + goto_val += test_goto4 (0, 0);
>>> + goto_val += test_goto4 (0, 1);
>>> + goto_val += test_goto4 (1, 0);
>>> + goto_val += test_goto4 (1, 1);
>>> +
>>> + goto_val += test_goto5 (1);
>>> + goto_val += test_goto6 (1);
>>> +}
>>> +
>>> +/* Returns, guarded by both plain values and function calls.
>>> +
>>> + This test demonstrates a surprising behaviour; because there are
>>> no braces
>>> + but a single return statement, the count is assigned to the
>>> pragma line and
>>> + not the return statement in the body. If we don't store the
>>> pragma location
>>> + we won't even record the return; as ignored. In a perfect world
>>> this would
>>> + be fixed by anchoring the return to the return statement itself
>>> and not the
>>> + if. If we add braces or return a value (see return2) this works as
>>> + expected. */
>>> +void
>>> +return1 (int a, int b, int c)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + if (a) return; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +#pragma GCC suppress_coverage begin
>>> + if (do_something (b)) return; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + if (do_something (c)) /* count(1) */
>>> +#pragma GCC suppress_coverage begin /* count(#) */
>>> + return; /* count(-) */
>>> +#pragma GCC suppress_coverage end
>>> + if (do_something (c)) { /* count(#####) */
>>> +#pragma GCC suppress_coverage begin /* count(-) */
>>> + return; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +}
>>> +
>>> +int
>>> +return2 (int a, int b, int c)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + if (a) return a; /* count(#) */
>>> +#pragma GCC suppress_coverage end /* count(-) */
>>> +#pragma GCC suppress_coverage begin /* count(-) */
>>> + if (do_something (b)) return b; /* count(#) */
>>> +#pragma GCC suppress_coverage end /* count(-) */
>>> + if (do_something (c)) /* count(1) */
>>> +#pragma GCC suppress_coverage begin /* count(-) */
>>> + return c; /* count(#) */
>>> +#pragma GCC suppress_coverage end /* count(-) */
>>> + return 0;
>>> +}
>>> +
>>> +void
>>> +call_return ()
>>> +{
>>> + return1 (1, 0, 0);
>>> + return1 (0, 1, 0);
>>> + return1 (0, 0, 1);
>>> + return2 (1, 0, 0);
>>> + return2 (0, 1, 0);
>>> + return2 (0, 0, 1);
>>> +}
>>> +
>>> +/* From gcov-6.c */
>>> +int test_exit_val;
>>> +
>>> +void
>>> +test_exit1 (int i)
>>> +{
>>> + /* An abnormal exit should not break suppression. */
>>> +#pragma GCC suppress_coverage begin
>>> + if (i < 0) /* count(#) */
>>> + exit (0); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + test_exit_val += i; /* count(3) */
>>> +}
>>> +
>>> +void
>>> +test_exit2 (int i)
>>> +{
>>> + if (i < 0) /* count(4) */
>>> +#pragma GCC suppress_coverage begin
>>> + exit (0); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + test_exit_val += i; /* count(3) */
>>> +}
>>> +
>>> +void
>>> +test_exit3 (int i)
>>> +{
>>> + /* There can be statements on either side of exit (). */
>>> +#pragma GCC suppress_coverage begin
>>> + if (i < 0) /* count(#) */
>>> + {
>>> + test_exit_val += i; /* count(#) */
>>> + exit (0); /* count(#) */
>>> + test_exit_val += i; /* count(-) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + test_exit_val += i; /* count(3) */
>>> +}
>>> +
>>> +void
>>> +call_exit ()
>>> +{
>>> + for (int i = 0; i != 3; ++i)
>>> + test_exit1 (i);
>>> + for (int i = 0; i != 3; ++i)
>>> + test_exit2 (i);
>>> + for (int i = 0; i != 3; ++i)
>>> + test_exit3 (i);
>>> +
>>> + test_exit2 (-1);
>>> +}
>>> +
>>> +int
>>> +computed_goto1 (int a)
>>> +{
>>> + void *op;
>>> +#pragma GCC suppress_coverage begin
>>> + op = &&dest; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> +dest:
>>> + if (op && a > 0) /* count(6) */
>>> + {
>>> + a -= 1; /* count(5) */
>>> + goto *op; /* count(5) */
>>> + }
>>> +
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +computed_goto2 (int a)
>>> +{
>>> + void *op = &&dest; /* count(1) */
>>> +dest:
>>> + ;
>>> +#pragma GCC suppress_coverage begin
>>> + if (op && a > 0) /* count(#) */
>>> + {
>>> + a -= 1; /* count(#) */
>>> + goto *op; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return a;
>>> +}
>>> +
>>> +int
>>> +computed_goto3 (int a)
>>> +{
>>> + void *op = &&dest; /* count(1) */
>>> +dest:
>>> + ;
>>> + if (op && a > 0) /* count(6) */
>>> + {
>>> + a -= 1; /* count(5) */
>>> +#pragma GCC suppress_coverage begin
>>> + goto *op; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +
>>> + return a;
>>> +}
>>> +
>>> +void
>>> +call_computed_goto ()
>>> +{
>>> + computed_goto1 (5);
>>> + computed_goto2 (5);
>>> + computed_goto3 (5);
>>> +}
>>> +
>>> +__attribute__((suppress_coverage)) int
>>> +suppressed_function (int a)
>>> +{
>>> + int c; /* count(-) */
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + return c; /* count(#) */
>>> +}
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> +int
>>> +pragma_begin_outside (int a)
>>> +{
>>> + int c; /* count(-) */
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + return c; /* count(#) */
>>> +}
>>> +#pragma GCC suppress_coverage end
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> +int
>>> +pragma_end_middle (int a)
>>> +{
>>> + int c; /* count(-) */
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + int d = a - 1; /* count(1) */
>>> + c = a+b+d; /* count(1) */
>>> + return c; /* count(1) */
>>> +}
>>> +
>>> +void
>>> +empty1 ()
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> +#pragma GCC suppress_coverage end
>>> +}
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> +void
>>> +empty2 ()
>>> +{
>>> +#pragma GCC suppress_coverage end
>>> +}
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> +void
>>> +empty3 ()
>>> +{
>>> +}
>>> +#pragma GCC suppress_coverage end
>>> +
>>> +__attribute__((suppress_coverage))
>>> +void
>>> +empty4 ()
>>> +{
>>> +}
>>> +
>>> +void
>>> +call_empty ()
>>> +{
>>> + empty1 ();
>>> + empty2 ();
>>> + empty3 ();
>>> + empty4 ();
>>> +}
>>> +
>>> +int main ()
>>> +{
>>> + empty_body_for_loop ();
>>> + ignored_for_loop ();
>>> + declarations (1);
>>> + compound_statements (1);
>>> + call_while ();
>>> + call_for ();
>>> + call_ifelse ();
>>> + call_switch ();
>>> + call_goto ();
>>> + call_return ();
>>> + call_computed_goto ();
>>> + suppressed_function (1);
>>> + pragma_begin_outside (1);
>>> + pragma_end_middle (1);
>>> + call_empty ();
>>> +
>>> + /* The final test will actually exit, so make sure to call it
>>> last. */
>>> + call_exit ();
>>> + return 0;
>>> +}
>>> +
>>> +/* { dg-final { run-gcov { gcov-35.c } } } */
>>> diff --git a/gcc/testsuite/gcc.misc-tests/gcov-36.c b/gcc/testsuite/
>>> gcc.misc-tests/gcov-36.c
>>> new file mode 100644
>>> index 000000000000..0805a3336e1f
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.misc-tests/gcov-36.c
>>> @@ -0,0 +1,23 @@
>>> +/* { dg-options "--coverage -std=c23" } */
>>> +/* { dg-do run } */
>>> +
>>> +/* [[attribute]] syntax instead of __attribute__((attr)). */
>>> +
>>> +[[gnu::suppress_coverage]] int
>>> +suppressed_function (int a)
>>> +{
>>> + int c; /* count(-) */
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + return c; /* count(#) */
>>> +}
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + suppressed_function (1);
>>> +}
>>> +
>>> +/* { dg-final { run-gcov gcov-36.c } } */
>>> diff --git a/gcc/testsuite/gcc.misc-tests/gcov-37.c b/gcc/testsuite/
>>> gcc.misc-tests/gcov-37.c
>>> new file mode 100644
>>> index 000000000000..9e2e62615f7d
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.misc-tests/gcov-37.c
>>> @@ -0,0 +1,243 @@
>>> +/* { dg-options "--coverage -fpath-coverage" } */
>>> +/* { dg-do run } */
>>> +
>>> +/* An obvious use case of #pragma GCC suppress_coverage is to support
>>> + contracts/pre- and post-conditions, without having the
>>> contradictions
>>> + messing up the coverage reports.
>>> +
>>> + The tests are written in terms of prime path coverage, because it
>>> more
>>> + accurately detects when things are *not* included. Here's how
>>> gcov -b would
>>> + report the branches:
>>> +
>>> + #: 13: REQUIRE(x >= 0); // branch(100)
>>> + branch 0 taken 100%
>>> + // branch(end)
>>> +
>>> + The branch test would not detect if gcov had also printed the
>>> + should-be-suppressed-branch:
>>> +
>>> + branch 0 taken 100%
>>> + branch 1 taken 0%
>>> +*/
>>> +
>>> +int identity (int x) { return x; }
>>> +
>>> +#define REQUIRE(pred) do { \
>>> + _Pragma ("GCC suppress_coverage begin") \
>>> + if (!(pred)) return -1; \
>>> + _Pragma ("GCC suppress_coverage end") \
>>> + } while (0)
>>> +#define ENSURE(pred) do { \
>>> + _Pragma ("GCC suppress_coverage begin") \
>>> + if (!(pred)) return -1; \
>>> + _Pragma ("GCC suppress_coverage end") \
>>> + } while (0)
>>> +
>>> +/* BEGIN paths
>>> + summary: 1/1
>>> + expect covered: 43(suppress) 48(suppress) 50
>>> +
>>> + There are really 5 prime paths through this function, but 4 of
>>> them should
>>> + be suppressed. */
>>> +int
>>> +contracts1 (int x, int y)
>>> +/* END */
>>> +{
>>> + REQUIRE (x >= 0);
>>> + REQUIRE (y >= 0);
>>> + int z = x + y;
>>> + ENSURE (z >= x && z >= y);
>>> + return z;
>>> +}
>>> +
>>> +/* BEGIN paths
>>> + summary: 0/1
>>> + expect: 60(suppress) 65(suppress) 67
>>> +
>>> + We're failing a precondition, which should not contribute to
>>> coverage
>>> +*/
>>> +int
>>> +contracts2 (int x, int y)
>>> +/* END */
>>> +{
>>> + REQUIRE (x >= 0);
>>> + REQUIRE (y >= 0);
>>> + int z = x + y;
>>> + ENSURE (z >= x && z >= y);
>>> + return z;
>>> +}
>>> +
>>> +/* BEGIN paths
>>> + summary: 11/14
>>> +
>>> + This is the reference function. It's body should be identical to
>>> + disable_in_loopN. All functions should be called with the same
>>> + arguments, but disable different parts of the function.
>>> + [[gnu::suppress_coverage]] may change the graph (insert blocks),
>>> so the
>>> + number of paths may change slightly. */
>>> +int
>>> +suppressed_in_loop (int len)
>>> +/* END */
>>> +{
>>> + int x = len;
>>> + x = identity (x);
>>> + x *= 5;
>>> + for (int i = 0; i < len; ++i)
>>> + {
>>> + x += identity (i);
>>> +
>>> + if (i > 5)
>>> + x += 1;
>>> + }
>>> +
>>> + return x;
>>> +}
>>> +
>>> +/* BEGIN paths
>>> + summary: 9/12
>>> +
>>> + We're definitely expecting not taking any path from the top into
>>> the THEN of
>>> + (i > 5).
>>> + expect: 108 110 110(suppress) 116(true) 117 110
>>> +*/
>>> +int
>>> +suppressed_in_loop1 (int len)
>>> +/* END */
>>> +{
>>> + int x = len;
>>> + x = identity (x);
>>> + x *= 5;
>>> + for (int i = 0; i < len; ++i)
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + x += identity (i);
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + if (i > 5)
>>> + x += 1;
>>> + }
>>> +
>>> + return x;
>>> +}
>>> +
>>> +/* BEGIN paths
>>> + summary: 6/7 */
>>> +int
>>> +suppressed_in_loop2 (int len)
>>> +/* END */
>>> +{
>>> + int x = len;
>>> + x = identity (x);
>>> + x *= 5;
>>> + for (int i = 0; i < len; ++i)
>>> + {
>>> + x += identity (i);
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + if (i > 5)
>>> + x += 1;
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> +
>>> + return x;
>>> +}
>>> +
>>> +/* BEGIN paths
>>> + summary: 0/1
>>> +
>>> + By disabling the full loop we should only have a single path
>>> through the
>>> + function, as-if the loop isn't there. */
>>> +int
>>> +suppressed_in_loop3 (int len)
>>> +/* END */
>>> +{
>>> + int x = len;
>>> + x = identity (x);
>>> + x *= 5;
>>> +#pragma GCC suppress_coverage begin
>>> + for (int i = 0; i < len; ++i)
>>> + {
>>> + x += identity (i);
>>> +
>>> + if (i > 5)
>>> + x += 1;
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + return x;
>>> +}
>>> +
>>> +/* BEGIN paths
>>> + summary: 1/8
>>> + args: (0, 0, 0, 0)
>>> +
>>> + Killing the first if should remove a single path only. */
>>> +void
>>> +pathcov004c (int a, int b, int c, int d)
>>> +/* END */
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + if (a)
>>> + {
>>> + }
>>> + else
>>> + /* We cannot syntactically put the pragma before the else, but
>>> since the
>>> + control flow is associated with the then-statement and not
>>> the else
>>> + keyword, putting the end after else works fine. */
>>> +#pragma GCC suppress_coverage end
>>> + {
>>> + while (b-- > 0 && c-- > 0)
>>> + {
>>> + if (d)
>>> + break;
>>> + }
>>> + }
>>> +}
>>> +
>>> +/* BEGIN paths
>>> + args: (0, 1, 0, 0)
>>> + summary: 0/5
>>> +
>>> + We keep five paths:
>>> + if (a) -> while -> if (d) -> exit
>>> + if (a) -> exit
>>> +
>>> + if (d) -> while -> if (d)
>>> + if (d) -> while -> exit
>>> + while -> if (d) -> while
>>> +
>>> + We really only disable the loop condition and not the implicit
>>> jumps. */
>>> +
>>> +void
>>> +pathcov004d (int a, int b, int c, int d)
>>> +/* END */
>>> +{
>>> + if (a)
>>> + {}
>>> + else
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + while (b-- > 0 && c-- > 0)
>>> +#pragma GCC suppress_coverage end
>>> + {
>>> + if (d)
>>> + break;
>>> + }
>>> + }
>>> +}
>>> +
>>> +int
>>> +main ()
>>> +{
>>> + contracts1 (2, 4);
>>> + contracts2 (-2, 4);
>>> + contracts2 (2, -4);
>>> + suppressed_in_loop (10);
>>> + suppressed_in_loop1 (10);
>>> + suppressed_in_loop2 (10);
>>> + suppressed_in_loop3 (10);
>>> + pathcov004c (0, 0, 0, 0);
>>> + pathcov004d (0, 1, 0, 0);
>>> +}
>>> +
>>> +/* { dg-final { run-gcov prime-paths { --prime-paths-lines=both
>>> gcov-37.c } } } */
>>> diff --git a/gcc/testsuite/gcc.misc-tests/gcov-38.c b/gcc/testsuite/
>>> gcc.misc-tests/gcov-38.c
>>> new file mode 100644
>>> index 000000000000..06d921581b60
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.misc-tests/gcov-38.c
>>> @@ -0,0 +1,100 @@
>>> +/* { dg-options "--coverage" } */
>>> +/* { dg-do run } */
>>> +/* { dg-require-effective-target indirect_jumps } */
>>> +
>>> +#include <setjmp.h>
>>> +extern void abort (void);
>>> +extern void exit (int);
>>> +
>>> +jmp_buf longjmp1_env;
>>> +int longjmp1_val;
>>> +int longjmp1_taken;
>>> +int longjmp1_bar_enter, longjmp1_bar_exit;
>>> +int longjmp1_foo_enter, longjmp1_foo_exit;
>>> +
>>> +/* Based on gcov-7.c */
>>> +
>>> +void
>>> +longjmp1_bar (int i)
>>> +{
>>> + longjmp1_bar_enter++; /* count(3) */
>>> + if (i == 0) {
>>> +#pragma GCC suppress_coverage begin
>>> + longjmp1_taken++; /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + longjmp (longjmp1_env, 1); /* count(1) */
>>> + }
>>> + longjmp1_val += i+1; /* count(2) */
>>> + longjmp1_bar_exit++; /* count(2) */
>>> +}
>>> +
>>> +void
>>> +longjmp1_foo (int i)
>>> +{
>>> + longjmp1_foo_enter++; /* count(3) */
>>> + if (i == 1) {
>>> + longjmp1_taken++; /* count(1) */
>>> +#pragma GCC suppress_coverage begin
>>> + longjmp (longjmp1_env, 2); /* count(#) */
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + longjmp1_bar (i); /* count(2) */
>>> + longjmp1_bar (7); /* count(1) */
>>> + longjmp1_val += 16;
>>> + longjmp1_foo_exit++; /* count(1) */
>>> +}
>>> +
>>> +void
>>> +longjmp1 ()
>>> +{
>>> + int retlongjmp1_val;
>>> +#pragma GCC suppress_coverage begin
>>> + if ((retlongjmp1_val = setjmp (longjmp1_env))) {
>>> + longjmp1_val += retlongjmp1_val; /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + longjmp1_foo (longjmp1_val); /* count(3) */
>>> +
>>> + if (!(longjmp1_val == 31 &&
>>> + longjmp1_taken == 2 &&
>>> + longjmp1_foo_enter == 3 &&
>>> + longjmp1_foo_exit == 1 &&
>>> + longjmp1_bar_enter == 3 &&
>>> + longjmp1_bar_exit == 2))
>>> + abort ();
>>> +}
>>> +
>>> +/* Based on pr85372.c */
>>> +void *buf[5];
>>> +
>>> +void fjmp (void) {
>>> + __builtin_longjmp (buf, 1);
>>> +}
>>> +
>>> +int
>>> +pr85372 (void)
>>> +{
>>> + int last = 0;
>>> +
>>> + if (__builtin_setjmp (buf) == 0) { /* count(2) */
>>> + __builtin_printf("True branch\n");
>>> +#pragma GCC suppress_coverage begin
>>> + while (1) {
>>> + last = 1; /* count(#) */
>>> + fjmp (); /* count(#) */
>>> + }
>>> +#pragma GCC suppress_coverage end
>>> + } else {
>>> + __builtin_printf("False branch\n");
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +int main ()
>>> +{
>>> + longjmp1 ();
>>> + pr85372 ();
>>> +}
>>> +
>>> +/* { dg-final { run-gcov { gcov-38.c } } } */
>>> diff --git a/gcc/testsuite/gcc.misc-tests/gcov-39.c b/gcc/testsuite/
>>> gcc.misc-tests/gcov-39.c
>>> new file mode 100644
>>> index 000000000000..d00713da0140
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.misc-tests/gcov-39.c
>>> @@ -0,0 +1,35 @@
>>> +/* { dg-do run } */
>>> +
>>> +/* #pragma GCC suppress_coverage should be a no-op and harmless when
>>> coverage is
>>> + not enabled (--coverage, -fcondition-coverage, etc.). */
>>> +
>>> +int do_something (int x) {
>>> + return x;
>>> +}
>>> +
>>> +int main (int argc, char **argv)
>>> +{
>>> +#pragma GCC suppress_coverage begin
>>> + int b = argc + 1;
>>> +#pragma GCC suppress_coverage end
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + int c;
>>> +#pragma GCC suppress_coverage end
>>> +
>>> + int a = argc;
>>> +
>>> + if (a)
>>> + if (b)
>>> + {
>>> +#pragma GCC suppress_coverage begin
>>> + c = do_something (4);
>>> +#pragma GCC suppress_coverage end
>>> + }
>>> + else
>>> + c = do_something (1024);
>>> +
>>> +#pragma GCC suppress_coverage begin
>>> + int d = a + c - 1;
>>> +#pragma GCC suppress_coverage end
>>> +}
>>> diff --git a/gcc/testsuite/gcc.misc-tests/gcov-40.c b/gcc/testsuite/
>>> gcc.misc-tests/gcov-40.c
>>> new file mode 100644
>>> index 000000000000..0d91c41e62fc
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.misc-tests/gcov-40.c
>>> @@ -0,0 +1,43 @@
>>> +/* { dg-options "--coverage" } */
>>> +/* { dg-do run } */
>>> +
>>> +/* We should be able to use the pragma even through a macro, also
>>> when that
>>> + macro is defined in a different file. */
>>> +#define SUPPRESS_COVERAGE _Pragma ("GCC suppress_coverage begin")
>>> +#define ENABLE_COVERAGE _Pragma ("GCC suppress_coverage end")
>>> +
>>> +#include "gcov-40.h"
>>> +
>>> +int
>>> +fn1 (int a)
>>> +{
>>> + int c;
>>> + SUPPRESS_COVERAGE
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + ENABLE_COVERAGE
>>> + return c; /* count(1) */
>>> +}
>>> +
>>> +int
>>> +fn2 (int a)
>>> +{
>>> + int c;
>>> + INCLUDE_SUPPRESS_COVERAGE
>>> + int b = a + 1; /* count(#) */
>>> + a *= 2; /* count(#) */
>>> + int d = a - 1; /* count(#) */
>>> + c = a+b+d; /* count(#) */
>>> + INCLUDE_ENABLE_COVERAGE
>>> + return c; /* count(1) */
>>> +}
>>> +
>>> +int main ()
>>> +{
>>> + fn1 (1);
>>> + fn2 (2);
>>> +}
>>> +
>>> +/* { dg-final { run-gcov gcov-40.c } } */
>>> diff --git a/gcc/testsuite/gcc.misc-tests/gcov-40.h b/gcc/testsuite/
>>> gcc.misc-tests/gcov-40.h
>>> new file mode 100644
>>> index 000000000000..cf44168b4c9c
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.misc-tests/gcov-40.h
>>> @@ -0,0 +1,4 @@
>>> +#define INCL_PUSH _Pragma ("GCC suppress_coverage begin")
>>> +#define INCL_POP _Pragma ("GCC suppress_coverage end")
>>> +#define INCLUDE_SUPPRESS_COVERAGE INCL_PUSH
>>> +#define INCLUDE_ENABLE_COVERAGE INCL_POP
>>> diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc
>>> index dc07dde8cfd4..4a2fca078442 100644
>>> --- a/gcc/tree-cfg.cc
>>> +++ b/gcc/tree-cfg.cc
>>> @@ -133,6 +133,7 @@ static bool gimple_verify_flow_info (void);
>>> static void gimple_make_forwarder_block (edge);
>>> static bool verify_gimple_transaction (gtransaction *);
>>> static bool call_can_make_abnormal_goto (gimple *);
>>> +static bool gimple_empty_block_p (basic_block);
>>> /* Flowgraph optimization and cleanup. */
>>> static void gimple_merge_blocks (basic_block, basic_block);
>>> @@ -492,6 +493,7 @@ make_blocks_1 (gimple_seq seq, basic_block bb)
>>> gimple *prev_stmt = NULL;
>>> bool start_new_block = true;
>>> bool first_stmt_of_seq = true;
>>> + bool suppress_coverage_p = false;
>>> while (!gsi_end_p (i))
>>> {
>>> @@ -510,6 +512,13 @@ make_blocks_1 (gimple_seq seq, basic_block bb)
>>> if (stmt && is_gimple_call (stmt))
>>> gimple_call_initialize_ctrl_altering (stmt);
>>> + suppress_coverage_p = in_pragma_suppress_coverage_p (stmt,
>>> + suppress_coverage_p);
>>> + if (suppress_coverage_p && !coverage_suppressed_p (bb))
>>> + start_new_block = true;
>>> + else if (!suppress_coverage_p && coverage_suppressed_p (bb))
>>> + start_new_block = true;
>>> +
>>> /* If the statement starts a new basic block or if we have
>>> determined
>>> in a previous pass that we need to create a new block for
>>> STMT, do
>>> so now. */
>>> @@ -518,6 +527,7 @@ make_blocks_1 (gimple_seq seq, basic_block bb)
>>> if (!first_stmt_of_seq)
>>> gsi_split_seq_before (&i, &seq);
>>> bb = create_basic_block (seq, bb);
>>> +
>>> start_new_block = false;
>>> prev_stmt = NULL;
>>> }
>>> @@ -526,6 +536,9 @@ make_blocks_1 (gimple_seq seq, basic_block bb)
>>> codes. */
>>> gimple_set_bb (stmt, bb);
>>> + if (suppress_coverage_p)
>>> + suppress_coverage (bb);
>>> +
>>> /* If STMT is a basic block terminator, set START_NEW_BLOCK
>>> for the
>>> next iteration. */
>>> if (stmt_ends_bb_p (stmt))
>>> @@ -622,6 +635,44 @@ make_blocks (gimple_seq seq)
>>> }
>>> }
>>> + /* If the function decl itself is in a #pragma GCC
>>> suppress_coverage block we
>>> + insert a nop with the same location as the function so that we're
>>> + guaranteed that the first block gets coverage suppressed and
>>> the function
>>> + entry isn't counted.
>>> +
>>> + #pragma GCC suppress_coverage begin
>>> + int foo (args) // count = suppressed
>>> + {
>>> + #pragma GCC suppress_coverage end
>>> + ...
>>> + }
>>> +
>>> + If it isn't and the first stmt is suppressed we insert a nop to
>>> ensure that
>>> + the function entry is counted, but the first (real) stmt is not:
>>> +
>>> + int foo (args) // count = 1
>>> + {
>>> + #pragma GCC suppress_coverage begin
>>> + bar (); // count = suppressed
>>> + #pragma GCC suppress_coverage end
>>> + ...
>>> + }
>>> +
>>> + gimple_can_merge_blocks_p is aware of specific instruction. */
>>> + gimple_stmt_iterator gsi = gsi_start (seq);
>>> + const location_t decl_loc = DECL_SOURCE_LOCATION (cfun->decl);
>>> + if (location_in_pragma_suppress_coverage_p (decl_loc))
>>> + {
>>> + gimple *nop = gimple_build_nop ();
>>> + gimple_set_location (nop, decl_loc);
>>> + gsi_insert_seq_before (&gsi, nop, GSI_NEW_STMT);
>>> + }
>>> + else if (*gsi && in_pragma_suppress_coverage_p (*gsi, false))
>>> + {
>>> + gimple *nop = gimple_build_nop ();
>>> + gsi_insert_seq_before (&gsi, nop, GSI_NEW_STMT);
>>> + }
>>> +
>>> make_blocks_1 (seq, ENTRY_BLOCK_PTR_FOR_FN (cfun));
>>> }
>>> @@ -1827,6 +1878,25 @@ gimple_can_merge_blocks_p (basic_block a,
>>> basic_block b)
>>> if (stmt && stmt_ends_bb_p (stmt))
>>> return false;
>>> + /* Check if the compiler-inserted nop should block merges. */
>>> + if (stmt && gimple_nop_p (stmt)
>>> + && gimple_location (stmt) == DECL_SOURCE_LOCATION (cfun->decl)
>>> + && coverage_suppressed_p (a) && !coverage_suppressed_p (b))
>>> + return false;
>>> +
>>> + /* We cannot merge blocks if one has suppressed coverage and the
>>> other hasn't.
>>> + The exception is when the merge-into block is empty AND is the
>>> one with
>>> + coverage suppressed. This can happen for code like this:
>>> +
>>> + while (cond)
>>> + #pragma GCC suppress_coverage begin
>>> + foo ();
>>> + #pragma GCC suppress_coverage end
>>> + */
>>> + if (coverage_suppressed_p (a) != coverage_suppressed_p (b)
>>> + && !(coverage_suppressed_p (a) && gimple_empty_block_p (a)))
>>> + return false;
>>> +
>>> /* Examine the labels at the beginning of B. */
>>> for (gimple_stmt_iterator gsi = gsi_start_bb (b); !gsi_end_p (gsi);
>>> gsi_next (&gsi))
>>> @@ -1972,6 +2042,10 @@ gimple_merge_blocks (basic_block a,
>>> basic_block b)
>>> gimple_stmt_iterator last, gsi;
>>> gphi_iterator psi;
>>> + if (coverage_suppressed_p (a) && !coverage_suppressed_p (b)
>>> + && gimple_empty_block_p (a))
>>> + suppress_coverage_unset (a);
>>> +
>>> if (dump_file)
>>> fprintf (dump_file, "Merging blocks %d and %d\n", a->index, b-
>>> >index);
>>> @@ -2889,6 +2963,8 @@ gimple_split_edge (edge edge_in)
>>> new_bb = create_empty_bb (after_bb);
>>> new_bb->count = edge_in->count ();
>>> + if (coverage_suppressed_p (after_bb))
>>> + suppress_coverage (new_bb);
>>> /* We want to avoid re-allocating PHIs when we first
>>> add the fallthru edge from new_bb to dest but we also
>>> @@ -6275,6 +6351,8 @@ gimple_split_block (basic_block bb, void *stmt)
>>> edge_iterator ei;
>>> new_bb = create_empty_bb (bb);
>>> + if (coverage_suppressed_p (bb))
>>> + suppress_coverage (new_bb);
>>> /* Redirect the outgoing edges. */
>>> new_bb->succs = bb->succs;
>>> @@ -6431,6 +6509,8 @@ gimple_duplicate_bb (basic_block bb,
>>> copy_bb_data *id)
>>> gimple_stmt_iterator gsi_tgt;
>>> new_bb = create_empty_bb (EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb);
>>> + if (coverage_suppressed_p (bb))
>>> + suppress_coverage (new_bb);
>>> /* Copy the PHI nodes. We ignore PHI node arguments here because
>>> the incoming edges have not been setup yet. */
>>> @@ -9474,6 +9554,8 @@ insert_cond_bb (basic_block bb, gimple *stmt,
>>> gimple *cond,
>>> /* Create conditionally executed block. */
>>> new_bb = create_empty_bb (bb);
>>> + if (coverage_suppressed_p (bb))
>>> + suppress_coverage (new_bb);
>>> edge e = make_edge (bb, new_bb, EDGE_TRUE_VALUE);
>>> e->probability = prob;
>>> new_bb->count = e->count ();
>>
>> Ping.
>>
>> Joseph, I added some tests for the pragma diagnostics (which was good
>> because it lead to much improved phrasing, I think).
>>
>> Is it otherwise ok for trunk? Bootstrapped and regtested on x86_64-
>> linux-gnu.
>>
>> Thanks,
>> Jørgen
>
> Ping.
Ping.