From 29265ace28cd61aae66513d520a47744d00ef023 Mon Sep 17 00:00:00 2001
From: Sebastian Huber <sebastian.huber@embedded-brains.de>
Date: Fri, 6 Aug 2021 17:22:19 +0200
Subject: [PATCH] Back port v8 of gcov improvements to GCC 10

Remove wrong bail out in prune_topn_counter.

	* libgcov-driver.c (prune_topn_counter): Remove
	check for -1 as we only prune run-time counters
	that do not generate an invalid state.

Make TOPN counter dynamically allocated.

gcc/ChangeLog:

	* coverage.c (get_coverage_counts): Skip sanity check for TOP N counters
	as they have variable number of counters.
	* gcov-dump.c (main): Add new option -r.
	(print_usage): Likewise.
	(tag_counters): All new raw format.
	* gcov-io.h (struct gcov_kvp): New.
	(GCOV_TOPN_VALUES): Remove.
	(GCOV_TOPN_VALUES_COUNTERS): Likewise.
	(GCOV_TOPN_MEM_COUNTERS): New.
	(GCOV_TOPN_DISK_COUNTERS): Likewise.
	(GCOV_TOPN_MAXIMUM_TRACKED_VALUES): Likewise.
	* ipa-profile.c (ipa_profile_generate_summary): Use
	GCOV_TOPN_MAXIMUM_TRACKED_VALUES.
	(ipa_profile_write_edge_summary): Likewise.
	(ipa_profile_read_edge_summary): Likewise.
	(ipa_profile): Remove usage of GCOV_TOPN_VALUES.
	* profile.c (sort_hist_values): Sort variable number
	of counters.
	(compute_value_histograms): Special case for TOP N counters
	that have dynamic number of key-value pairs.
	* value-prof.c (dump_histogram_value): Dump variable number
	of key-value pairs.
	(stream_in_histogram_value): Stream in variable number
	of key-value pairs for TOP N counter.
	(get_nth_most_common_value): Deal with variable number
	of key-value pairs.
	(dump_ic_profile): Use GCOV_TOPN_MAXIMUM_TRACKED_VALUES
	for loop iteration.
	(gimple_find_values_to_profile): Set GCOV_TOPN_MEM_COUNTERS
	to n_counters.
	* doc/gcov-dump.texi: Document new -r option.

libgcc/ChangeLog:

	* libgcov-driver.c (prune_topn_counter): Remove.
	(prune_counters): Likewise.
	(merge_one_data): Special case TOP N counters
	as they have variable length.
	(write_top_counters): New.
	(write_one_data): Special case TOP N.
	(dump_one_gcov): Do not prune TOP N counters.
	* libgcov-merge.c (merge_topn_values_set): Remove.
	(__gcov_merge_topn): Use gcov_topn_add_value.
	* libgcov-profiler.c (__gcov_topn_values_profiler_body):
	Likewise here.
	* libgcov.h (gcov_counter_add): New.
	(gcov_counter_set_if_null): Likewise.
	(gcov_topn_add_value): New.

libgcov: fix TOPN type casting

The patch fixes tree-prof.exp tests on solaris11 and i686-linux-gnu,
problem was that sizeof of a pointer is different from sizeof gcov_type.

I'm going to install it if there are no objections.
Thanks,
Martin

libgcc/ChangeLog:

	PR gcov-profile/95494
	* libgcov-driver.c (write_top_counters): Cast first to
	intptr_t as sizeof(*) != sizeof(gcov_type).
	* libgcov.h (gcov_counter_set_if_null): Remove.
	(gcov_topn_add_value): Cast first to intptr_t and update
	linked list directly.

gcov-tool: fix merge operation for summary

libgcc/ChangeLog:

	* libgcov-driver.c (merge_summary): Remove function as its name
	is misleading and doing something different.
	(dump_one_gcov): Add ATTRIBUTE_UNUSED for 2 args. Take read summary
	in gcov-tool.
	* libgcov-util.c (curr_object_summary): Remove.
	(read_gcda_file): Remove unused curr_object_summary.
	(gcov_merge): Merge summaries.
	* libgcov.h: Add summary argument for gcov_info struct.

Do not stream all zeros for gcda files.

gcc/ChangeLog:

	PR gcov-profile/95348
	* coverage.c (read_counts_file): Read only COUNTERS that are
	not all-zero.
	* gcov-dump.c (tag_function): Change signature from unsigned to
	signed integer.
	(tag_blocks): Likewise.
	(tag_arcs): Likewise.
	(tag_lines): Likewise.
	(tag_counters): Likewise.
	(tag_summary): Likewise.
	* gcov.c (read_count_file): Read all non-zero counters
	sensitively.

libgcc/ChangeLog:

	PR gcov-profile/95348
	* libgcov-driver.c (merge_one_data): Merge only profiles
	that are not of non-zero type.
	(write_one_data): Write counters only if there's one non-zero
	value.
	* libgcov-util.c (tag_function): Change signature from unsigned
	to int.
	(tag_blocks): Likewise.
	(tag_arcs): Likewise.
	(tag_counters): Likewise.
	(tag_summary): Likewise.
	(tag_lines): Read only if COUNTERS is non-zero.
	(read_gcda_file): Handle negative length for COUNTERS type.

libgcov: replace malloc and calloc.

The calloc was in the original tested version of the patch
and I made accidental last minute change.

Installed to master as obvious.

libgcc/ChangeLog:

	* libgcov.h (gcov_topn_add_value): Use xcalloc instead
	of xmalloc.

libgcov: support overloaded malloc

gcc/ChangeLog:

	* gcov-io.h (GCOV_PREALLOCATED_KVP): New.

libgcc/ChangeLog:

	* libgcov-driver.c: Add __gcov_kvp_pool
	and __gcov_kvp_pool_index variables.
	* libgcov.h (allocate_gcov_kvp): New.
	(gcov_topn_add_value): Use it.

gcc/testsuite/ChangeLog:

	* gcc.dg/tree-prof/indir-call-prof-malloc.c: New test.

gcov-profile: use static pool for TOPN first

gcc/ChangeLog:

	PR gcov-profile/97461
	* gcov-io.h (GCOV_PREALLOCATED_KVP): Pre-allocate 64
	static counters.

libgcc/ChangeLog:

	PR gcov-profile/97461
	* libgcov.h (gcov_counter_add): Use first static counters
	as it should help to have malloc wrappers set up.

gcc/testsuite/ChangeLog:

	PR gcov-profile/97461
	* gcc.dg/tree-prof/pr97461.c: New test.

gcov: use mmap pools for KVP.

gcc/ChangeLog:

	PR gcov-profile/97461
	* gcov-io.h (GCOV_PREALLOCATED_KVP): Remove.

libgcc/ChangeLog:

	PR gcov-profile/97461
	* config.in: Regenerate.
	* configure: Likewise.
	* configure.ac: Check sys/mman.h header file
	* libgcov-driver.c (struct gcov_kvp): Remove static
	pre-allocated pool and use a dynamic one.
	* libgcov.h (MMAP_CHUNK_SIZE): New.
	(gcov_counter_add): Use mmap to allocate pool for struct
	gcov_kvp.

gcov: call mmap MAP_ANONYMOUS with fd equal to -1

libgcc/ChangeLog:

	PR gcov-profile/99385
	* libgcov.h (allocate_gcov_kvp): Call mmap with fd equal to -1.

profiling: fix streaming of TOPN counters

libgcc/ChangeLog:

	PR gcov-profile/99105
	* libgcov-driver.c (write_top_counters): Rename to ...
	(write_topn_counters): ... this.
	(write_one_data): Pre-allocate buffer for number of items
	in the corresponding linked lists.
	* libgcov.h (malloc_mmap): New function.
	(allocate_gcov_kvp): Use it.

gcc/testsuite/ChangeLog:

	PR gcov-profile/99105
	* gcc.dg/tree-prof/indir-call-prof-malloc.c: Use profile
	correction as the wrapped malloc is called one more time
	from libgcov.
	* gcc.dg/tree-prof/pr97461.c: Likewise.

gcov: Add -fprofile-info-section support

Register the profile information in the specified section instead of using a
constructor/destructor.  A pointer to the profile information generated by
-fprofile-arcs or -ftest-coverage is placed in the specified section for each
translation unit.  This option disables the profile information registration
through a constructor and it disables the profile information processing
through a destructor.

I am not sure how I can test this option.  One approach would be to assemble a
test file, then scan it and check that a .gcov_info section is present and no
__gcov_init() and __gcov_exit() calls are present.  Is there an example for
this in the test suite?

gcc/

	* common.opt (fprofile-info-section): New.
	* coverage.c (build_gcov_info_var_registration): New.
	(coverage_obj_init): Evaluate profile_info_section and use
	build_gcov_info_var_registration().
	* doc/invoke.texi (fprofile-info-section): Document.
	* opts.c (common_handle_option): Process fprofile-info-section
	option.

gcc/testsuite/ChangeLog:

	* gcc.dg/profile-info-section.c: New test.

gcov: Fix use of profile info section

If the -fprofile-info-section is used, then the gcov information is registered
in a linker set.  This is done by build_gcov_info_var_registration().  The
compiler generated object placed in the section was not marked as referenced,
so once optimization was enabled, this object was optimized away.  Mark it as
referenced.

gcc/
	* coverage.c (build_gcov_info_var_registration): Mark the object placed
	in the linker set as referenced so that it does not get optimized away.

gcov: Add __gcov_info_to_gdca()

Add __gcov_info_to_gcda() to libgcov to get the gcda data for a gcda info in a
freestanding environment.  It is intended to be used with the
-fprofile-info-section option.  A crude test program which doesn't use a linker
script is (use "gcc -coverage -fprofile-info-section -lgcov test.c" to compile
it):

  #include <gcov.h>
  #include <stdio.h>
  #include <stdlib.h>

  extern const struct gcov_info *my_info;

  static void
  filename (const char *f, void *arg)
  {
    printf("filename: %s\n", f);
  }

  static void
  dump (const void *d, unsigned n, void *arg)
  {
    const unsigned char *c = d;

    for (unsigned i = 0; i < n; ++i)
      printf ("%02x", c[i]);
  }

  static void *
  allocate (unsigned length, void *arg)
  {
    return malloc (length);
  }

  int main()
  {
    __asm__ volatile (".set my_info, .LPBX2");
    __gcov_info_to_gcda (my_info, filename, dump, allocate, NULL);
    return 0;
  }

With this patch, <stdint.h> is included in libgcov-driver.c even if
inhibit_libc is defined.  This header file should be also available for
freestanding environments.  If this is not the case, then we have to define
intptr_t somehow.

The patch removes one use of memset() which makes the <string.h> include
superfluous.

gcc/

	* gcov-io.h (gcov_write): Declare.
	* gcov-io.c (gcov_write): New.
	(gcov_write_counter): Remove.
	(gcov_write_tag_length): Likewise.
	(gcov_write_summary): Replace gcov_write_tag_length() with calls to
	gcov_write_unsigned().
	* doc/invoke.texi (fprofile-info-section): Mention
	__gcov_info_to_gdca().

gcc/testsuite/

	* gcc.dg/gcov-info-to-gcda.c: New test.

libgcc/

	* Makefile.in (LIBGCOV_DRIVER): Add _gcov_info_to_gcda.
	* gcov.h (gcov_info): Declare.
	(__gcov_info_to_gdca): Likewise.
	* libgcov.h (gcov_write_counter): Remove.
	(gcov_write_tag_length): Likewise.
	* libgcov-driver.c (#include <stdint.h>): New.
	(#include <string.h>): Remove.
	(NEED_L_GCOV): Conditionally define.
	(NEED_L_GCOV_INFO_TO_GCDA): Likewise.
	(are_all_counters_zero): New.
	(gcov_dump_handler): Likewise.
	(gcov_allocate_handler): Likewise.
	(dump_unsigned): Likewise.
	(dump_counter): Likewise.
	(write_topn_counters): Add dump_fn, allocate_fn, and arg parameters.
	Use dump_unsigned() and dump_counter().
	(write_one_data): Add dump_fn, allocate_fn, and arg parameters.  Use
	dump_unsigned(), dump_counter(), and are_all_counters_zero().
	(__gcov_info_to_gcda): New.

gcov: Remove <stdint.h> from libgcov-driver.c

In the patch to add __gcov_info_to_gcda(), the include of <stdint.h> was added
to libgcov-driver.c even if inhibit_libc is defined.  It turned out that this
header file is not always available.  Remove the include of <stdint.h> and
replace the intptr_t with the compiler provided __INTPTR_TYPE__.

libgcc/

	* libgcov-driver.c (#include <stdint.h>): Remove.
	(write_topn_counters): Use __INTPTR_TYPE__ instead of intptr_t.

gcov: Add GCOV_TYPE_SIZE target macro

If -fprofile-update=atomic is used, then the target must provide atomic
operations for the counters of the type returned by get_gcov_type().
This is a 64-bit type for targets which have a 64-bit long long type.
On 32-bit targets this could be an issue since they may not provide
64-bit atomic operations.  Allow targets to override the default type
size with the new GCOV_TYPE_SIZE target macro.

gcc/

	* config/sparc/rtemself.h (GCOV_TYPE_SIZE): Define.
	* coverage.c (get_gcov_type): Use GCOV_TYPE_SIZE.
	* defaults.h (GCOV_TYPE_SIZE): Define default.
	* tree-profile.c (gimple_gen_edge_profiler): Use precision of
	gcov_type_node.
	(gimple_gen_time_profiler): Likewise.

libgcc/

	* libgcov.h (gcov_type): Define using GCOV_TYPE_SIZE.
	(gcov_type_unsigned): Likewise.

Use __builtin_trap() for abort() if inhibit_libc

abort() is used in gcc_assert() and gcc_unreachable() which is used by target
libraries such as libgcov.a.  This patch changes the abort() definition under
certain conditions.  If inhibit_libc is defined and abort is not already
defined, then abort() is defined to __builtin_trap().

The inhibit_libc define is usually defined if GCC is built for targets running
in embedded systems which may optionally use a C standard library.  If
inhibit_libc is defined, then there may be still a full featured abort()
available.  abort() is a heavy weight function which depends on signals and
file streams.  For statically linked applications, this means that a dependency
on gcc_assert() pulls in the support for signals and file streams.  This could
prevent using gcov to test low end targets for example.  Using __builtin_trap()
avoids these dependencies if the target implements a "trap" instruction.  The
application or operating system could use a trap handler to react to failed GCC
runtime checks which caused a trap.

gcc/

	* tsystem.h (abort): Define abort() if inhibit_libc is defined and it
	is not already defined.

Fix arm target build with inhibit_libc

Do not declare abort in "libgcc/unwind-arm-common.inc" since it is already
provided by "tsystem.h".  It fixes the following build error:

In file included from libgcc/config/arm/unwind-arm.c:144:
libgcc/unwind-arm-common.inc:55:24: error: macro "abort" passed 1 arguments, but takes just 0
   55 | extern void abort (void);

libgcc/

	* unwind-arm-common.inc (abort): Remove.

libgcov: Fix build on Darwin [PR99406]

As reported, bootstrap currently fails on older Darwin because MAP_ANONYMOUS
is not defined.

The following is what gcc/system.h does, so I think it should work for
libgcov.

2021-03-06  Jakub Jelinek  <jakub@redhat.com>

	PR gcov-profile/99406
	* libgcov.h (MAP_FAILED, MAP_ANONYMOUS): If HAVE_SYS_MMAN_H is
	defined, define these macros if not defined already.

Add parallelism support to gcov for MinGW platforms

If you attempt a profiled bootstrap on the MinGW platforms with -jN, N > 1,
it miserably fails because of profile mismatches all over the place, the
reason being that gcov has no support for parallelism on these platforms.

libgcc/
	* libgcov.h: For the target, define GCOV_LOCKED_WITH_LOCKING
	if __MSVCRT__ and, for the host, define it if HOST_HAS_LK_LOCK.
	* libgcov-driver.c: Add directives if GCOV_LOCKED_WITH_LOCKING.
gcc/
	* configure.ac: Check for the presence of sys/locking.h header and
	for whether _LK_LOCK is supported by _locking.
	* configure: Regenerate.
	* config.in: Likewise.
	* gcov-io.h: Define GCOV_LOCKED_WITH_LOCKING if HOST_HAS_LK_LOCK.
	* gcov-io.c (gcov_open): Add support for GCOV_LOCKED_WITH_LOCKING.
	* system.h: Include <sys/locking.h> if HAVE_SYS_LOCKING_H.

gcov: Use system IO buffering

gcc/ChangeLog:

	* gcov-io.c (gcov_write_block): Remove.
	(gcov_write_words): Likewise.
	(gcov_read_words): Re-implement using gcov_read_bytes.
	(gcov_allocate): Remove.
	(GCOV_BLOCK_SIZE): Likewise.
	(struct gcov_var): Remove most of the fields.
	(gcov_position): Implement with ftell.
	(gcov_rewrite): Remove setting of start and offset fields.
	(from_file): Re-format.
	(gcov_open): Remove setbuf call. It should not be needed.
	(gcov_close): Remove internal buffer handling.
	(gcov_magic): Use __builtin_bswap32.
	(gcov_write_counter): Use directly gcov_write_unsigned.
	(gcov_write_string): Use direct fwrite and do not round
	to 4 bytes.
	(gcov_seek): Use directly fseek.
	(gcov_write_tag): Use gcov_write_unsigned directly.
	(gcov_write_length): Likewise.
	(gcov_write_tag_length): Likewise.
	(gcov_read_bytes): Use directly fread.
	(gcov_read_unsigned): Use gcov_read_words.
	(gcov_read_counter): Likewise.
	(gcov_read_string): Use gcov_read_bytes.
	* gcov-io.h (GCOV_WORD_SIZE): Adjust to reflect
	that size is not in bytes, but words (4B).
	(GCOV_TAG_FUNCTION_LENGTH): Likewise.
	(GCOV_TAG_ARCS_LENGTH): Likewise.
	(GCOV_TAG_ARCS_NUM): Likewise.
	(GCOV_TAG_COUNTER_LENGTH): Likewise.
	(GCOV_TAG_COUNTER_NUM): Likewise.
	(GCOV_TAG_SUMMARY_LENGTH): Likewise.

libgcc/ChangeLog:

	* libgcov-driver.c: Fix GNU coding style.

gcov: update documentation entry about string format

gcc/ChangeLog:

	* gcov-io.h: Update documentation entry about string format.

gcov: update comment about padding

gcc/ChangeLog:

	* gcov-io.h: Remove padding entries.

gcov: check return code of a fclose

gcc/ChangeLog:

	PR gcov-profile/101773
	* gcov-io.c (gcov_close): Check return code of a fclose.

gcov: fix output location for JSON mode.

	PR gcov-profile/89961

gcc/ChangeLog:

	* gcov.c (make_gcov_file_name): Rewrite using std::string.
	(mangle_name): Simplify, do not used the second argument.
	(strip_extention): New function.
	(get_md5sum): Likewise.
	(get_gcov_intermediate_filename): Handle properly -p and -x
	options.
	(output_gcov_file): Use string type.
	(generate_results): Likewise.
	(md5sum_to_hex): Remove.

gcov: make profile merging smarter

Support merging of profiles that are built from a different .o files
but belong to the same source file. Moreover, a checksum is verified
during profile merging and so we can safely combine such profile.

	PR gcov-profile/90364

gcc/ChangeLog:

	* coverage.c (build_info): Emit checksum to the global variable.
	(build_info_type): Add new field for checksum.
	(coverage_obj_finish): Pass object_checksum.
	(coverage_init): Use 0 as checksum for .gcno files.
	* gcov-dump.c (dump_gcov_file): Dump also new checksum field.
	* gcov.c (read_graph_file): Read also checksum.
	* doc/invoke.texi: Document the behaviour change.

libgcc/ChangeLog:

	* libgcov-driver.c (merge_one_data): Skip timestamp and verify
	checksums.
	(write_one_data): Write also checksum.
	* libgcov-util.c (read_gcda_file): Read also checksum field.
	* libgcov.h (struct gcov_info): Add new field.

Remove unused but set variables.

Reported by clang13 -Wunused-but-set-variable:

gcc/ChangeLog:

	* dbgcnt.c (dbg_cnt_process_opt): Remove unused but set variable.
	* gcov.c (get_cycles_count): Likewise.
	* lto-compress.c (lto_compression_zlib): Likewise.
	(lto_uncompression_zlib): Likewise.
	* targhooks.c (default_pch_valid_p): Likewise.

libcpp/ChangeLog:

	* charset.c (convert_oct): Remove unused but set variable.

gcov: Remove dead variable.

gcc/ChangeLog:

	* gcov.c (read_line): Remove dead variable.

gcov-tool: Allow merging of empty profile lists

The gcov_profile_merge() already had code to deal with profile information
which had no counterpart to merge with.  For profile information from files
with no associated counterpart, the profile information is simply used as is
with the weighting transformation applied.  Make sure that gcov_profile_merge()
works with an empty target profile list.  Return the merged profile list.

gcc/
	* gcov-tool.cc (gcov_profile_merge): Adjust return type.
	(profile_merge): Allow merging of directories which contain no profile
	files.

libgcc/
	* libgcov-util.c (gcov_profile_merge): Return the list of merged
	profiles.  Accept empty target and source profile lists.

gcov: Add mode to all gcov_open()

gcc/

	* gcov-io.cc (gcov_open): Always use the mode parameter.
	* gcov-io.h (gcov_open): Declare it unconditionally.

libgcc/

	* libgcov-driver-system.c (gcov_exit_open_gcda_file): Open file for
	reading and writing.
	* libgcov-util.c (read_gcda_file): Open file for reading.
	* libgcov.h (gcov_open): Delete declaration.

gcov: Add open mode parameter to gcov_do_dump()

gcc/

	* gcov-tool.cc (gcov_do_dump): Add mode parameter.
	(gcov_output_files): Open files for reading and writing.

libgcc/

	* libgcov-driver-system.c (gcov_exit_open_gcda_file): Add mode
	parameter.  Pass mode to gcov_open() calls.
	* libgcov-driver.c (dump_one_gcov):  Add mode parameter.  Pass mode to
	gcov_exit_open_gcda_file() call.
	(gcov_do_dump): Add mode parameter.  Pass mode to dump_one_gcov()
	calls.
	(__gcov_dump_one):  Open file for reading and writing.

gcov: Make gcov_seek() static

This function is only used by gcov_write_length() in the gcov-io.cc file.

gcc/

	* gcov-io.cc (gcov_seek): Make it static.
	* gcov-io.h (struct gcov_summary): Do not mention gcov_seek().

libgcc/

	* libgcov.h (gcov_seek): Remove define and declaration.

gcov: Add __gcov_filename_to_gcfn()

gcc/

	* doc/invoke.texi (fprofile-info-section): Mention
	__gcov_filename_to_gcfn().  Use "freestanding" to match with C11
	standard language.  Fix minor example code issues.
	* gcov-io.h (GCOV_FILENAME_MAGIC): Define and document.

gcc/testsuite/

	* gcc.dg/gcov-info-to-gcda.c: Test __gcov_filename_to_gcfn().

libgcc/

	* gcov.h (__gcov_info_to_gcda): Mention __gcov_filename_to_gcfn().
	(__gcov_filename_to_gcfn): Declare and document.
	* libgcov-driver.c (dump_string): New.
	(__gcov_filename_to_gcfn): Likewise.
	(__gcov_info_to_gcda): Adjust comment to match C11 standard language.

gcov-tool: Support file input from stdin

gcc/

	* gcov-io.cc (GCOV_MODE_STDIN): Define.
	(gcov_position): For gcov-tool, return calculated position if file is
	stdin.
	(gcov_open):  For gcov-tool, use stdin if filename is NULL.
	(gcov_close): For gcov-tool, do not close stdin.
	(gcov_read_bytes): For gcov-tool, update position if file is stdin.
	(gcov_sync): For gcov-tool, discard input if file is stdin.

gcov: Use xstrdup()

Move duplication of filename to caller and use xstrdup() instead of custom
code.  This helps to reuse read_gcda_file() for other purposes.

libgcc/

	* libgcov-util.c (read_gcda_file): Do not duplicate filename.
	(ftw_read_file): Duplicate filename for read_gcda_file().

gcov: Move prepend to list to read_gcda_file()

This helps to reuse read_gcda_file().

libgcc/

	* libgcov-util.c (read_gcda_file): Prepend new info object to global
	list.
	(ftw_read_file): Remove list append here.

gcov: Move gcov_open() to caller of read_gcda_file()

This allows to reuse read_gcda_file() to read multiple objects from a single
file.

libgcc/

	* libgcov-util.c (read_gcda_file): Do not open file.
	(ftw_read_file): Open file here.

gcov: Fix integer types in ftw_read_file()

libgcc/

	* libgcov-util.c (ftw_read_file): Use size_t for strlen() variables.

gcov: Record EOF error during read

Use an enum for file error codes.

gcc/

	* gcov-io.cc (gcov_file_error): New enum.
	(gcov_var): Use gcov_file_error enum for the error member.
	(gcov_open): Use GCOV_FILE_NO_ERROR.
	(gcov_close): Use GCOV_FILE_WRITE_ERROR.
	(gcov_write): Likewise.
	(gcov_write_unsigned): Likewise.
	(gcov_write_string): Likewise.
	(gcov_read_bytes): Set error code if EOF is reached.
	(gcov_read_counter): Use GCOV_FILE_COUNTER_OVERFLOW.

gcov-tool: Add merge-stream subcommand

gcc/

	* doc/gcov-tool.texi: Document merge-stream subcommand.
	* doc/invoke.texi (fprofile-info-section): Mention merge-stream
	subcommand of gcov-tool.
	* gcov-tool.cc (gcov_profile_merge_stream): Declare.
	(print_merge_stream_usage_message): New.
	(merge_stream_usage): Likewise.
	(do_merge_stream): Likewise.
	(print_usage): Call print_merge_stream_usage_message().
	(main): Call do_merge_stream() to execute merge-stream subcommand.

libgcc/

	* libgcov-util.c (consume_stream): New.
	(get_target_profiles_for_merge): Likewise.
	(gcov_profile_merge_stream): Likewise.

gcov: Use xstrerror()

libgcc/

	* libgcov-util.c (ftw_read_file): Improve notice using xstrerror().
	(gcov_profile_merge_stream): Likewise.

libgcov: add ATTRIBUTE_UNUSED for dump_string

Mitigates the following clang warning:
libgcc/libgcov-driver.c:416:1: warning: unused function 'dump_string' [-Wunused-function]

libgcc/ChangeLog:

	* libgcov-driver.c: Add ATTRIBUTE_UNUSED.

gcov: Fix first time gcov info dump

This patch fixes an issue introduced by commit
ef9a53feae5701953da9161afef2aea0329ec8b2:

gcc --coverage main.c && ./a.out
libgcov profiling error:a-main.gcda:Error writing

gcc/ChangeLog:

	* gcov-io.cc (gcov_rewrite):  Clear the file error status.
---
 gcc/common.opt                                |   8 +
 gcc/config.in                                 |  42 ++
 gcc/config/sparc/rtemself.h                   |   2 +
 gcc/configure                                 | 179 +++++++-
 gcc/configure.ac                              |  81 +++-
 gcc/coverage.c                                |  99 +++--
 gcc/dbgcnt.c                                  |   2 -
 gcc/defaults.h                                |   8 +
 gcc/doc/gcov-dump.texi                        |   5 +
 gcc/doc/gcov-tool.texi                        |  36 ++
 gcc/doc/invoke.texi                           |  96 +++++
 gcc/gcov-dump.c                               |  74 ++--
 gcc/gcov-io.c                                 | 378 +++++++----------
 gcc/gcov-io.h                                 |  91 ++--
 gcc/gcov-tool.c                               | 107 ++++-
 gcc/gcov.c                                    | 179 ++++----
 gcc/ipa-profile.c                             |  11 +-
 gcc/lto-compress.c                            |   4 -
 gcc/opts.c                                    |   4 +
 gcc/profile.c                                 |  70 +--
 gcc/system.h                                  |   4 +
 gcc/targhooks.c                               |   4 +-
 gcc/testsuite/gcc.dg/gcov-info-to-gcda.c      |  82 ++++
 gcc/testsuite/gcc.dg/profile-info-section.c   |  20 +
 .../gcc.dg/tree-prof/indir-call-prof-malloc.c |  49 +++
 gcc/testsuite/gcc.dg/tree-prof/pr97461.c      |  58 +++
 gcc/tree-profile.c                            |   4 +-
 gcc/tsystem.h                                 |   2 +-
 gcc/value-prof.c                              |  59 ++-
 libcpp/charset.c                              |   2 -
 libgcc/ChangeLog                              |   2 +
 libgcc/Makefile.in                            |   2 +-
 libgcc/config.in                              |   3 +
 libgcc/configure                              |   6 +-
 libgcc/configure.ac                           |   2 +-
 libgcc/gcov.h                                 |  34 ++
 libgcc/libgcov-driver-system.c                |   7 +-
 libgcc/libgcov-driver.c                       | 401 +++++++++++++-----
 libgcc/libgcov-merge.c                        | 103 +----
 libgcc/libgcov-profiler.c                     |  40 +-
 libgcc/libgcov-util.c                         | 205 ++++++---
 libgcc/libgcov.h                              | 208 ++++++++-
 libgcc/unwind-arm-common.inc                  |   4 -
 43 files changed, 1983 insertions(+), 794 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/gcov-info-to-gcda.c
 create mode 100644 gcc/testsuite/gcc.dg/profile-info-section.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/pr97461.c

diff --git a/gcc/common.opt b/gcc/common.opt
index ec5235c3a41..65bcf4b518e 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2226,6 +2226,14 @@ fprofile-generate=
 Common Joined RejectNegative
 Enable common options for generating profile info for profile feedback directed optimizations, and set -fprofile-dir=.
 
+fprofile-info-section
+Common RejectNegative
+Register the profile information in the .gcov_info section instead of using a constructor/destructor.
+
+fprofile-info-section=
+Common Joined RejectNegative Var(profile_info_section)
+Register the profile information in the specified section instead of using a constructor/destructor.
+
 fprofile-partial-training
 Common Report Var(flag_profile_partial_training) Optimization
 Do not assume that functions never executed during the train run are cold.
diff --git a/gcc/config.in b/gcc/config.in
index 6de77edb446..3a726090a6c 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -320,6 +320,18 @@
 #endif
 
 
+/* Define if AF_INET6 supported. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_AF_INET6
+#endif
+
+
+/* Define if AF_UNIX supported. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_AF_UNIX
+#endif
+
+
 /* Define if your assembler supports architecture modifiers. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_AS_ARCHITECTURE_MODIFIERS
@@ -1532,6 +1544,12 @@
 #endif
 
 
+/* Define if the PE linker has broken DWARF 5 support. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_LD_BROKEN_PE_DWARF5
+#endif
+
+
 /* Define if your linker supports --build-id. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_LD_BUILDID
@@ -1599,6 +1617,12 @@
 #endif
 
 
+/* Define if the PE linker supports --disable-dynamicbase option. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_LD_PE_DISABLE_DYNAMICBASE
+#endif
+
+
 /* Define if your linker supports PIE option. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_LD_PIE
@@ -1847,6 +1871,12 @@
 #endif
 
 
+/* Define to 1 if you have the <sys/locking.h> header file. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_SYS_LOCKING_H
+#endif
+
+
 /* Define to 1 if you have the <sys/mman.h> header file. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_SYS_MMAN_H
@@ -2020,6 +2050,18 @@
 #endif
 
 
+/* Define if _LK_LOC supported by _locking. */
+#ifndef USED_FOR_TARGET
+#undef HOST_HAS_LK_LOCK
+#endif
+
+
+/* Define if O_CLOEXEC supported by fcntl. */
+#ifndef USED_FOR_TARGET
+#undef HOST_HAS_O_CLOEXEC
+#endif
+
+
 /* Define as const if the declaration of iconv() needs const. */
 #ifndef USED_FOR_TARGET
 #undef ICONV_CONST
diff --git a/gcc/config/sparc/rtemself.h b/gcc/config/sparc/rtemself.h
index ddec98c8384..be78570fd56 100644
--- a/gcc/config/sparc/rtemself.h
+++ b/gcc/config/sparc/rtemself.h
@@ -40,3 +40,5 @@
 
 /* Use the default */
 #undef LINK_GCC_C_SEQUENCE_SPEC
+
+#define GCOV_TYPE_SIZE 32
diff --git a/gcc/configure b/gcc/configure
index 53c27dba58e..c402258dc73 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -9040,7 +9040,7 @@ fi
 
 for ac_header in limits.h stddef.h string.h strings.h stdlib.h time.h iconv.h \
 		 fcntl.h ftw.h unistd.h sys/file.h sys/time.h sys/mman.h \
-		 sys/resource.h sys/param.h sys/times.h sys/stat.h \
+		 sys/resource.h sys/param.h sys/times.h sys/stat.h sys/locking.h \
 		 direct.h malloc.h langinfo.h ldfcn.h locale.h wchar.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
@@ -11963,6 +11963,179 @@ $as_echo "#define HOST_HAS_F_SETLKW 1" >>confdefs.h
 
 fi
 
+# Check if O_CLOEXEC is defined by fcntl
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for O_CLOEXEC" >&5
+$as_echo_n "checking for O_CLOEXEC... " >&6; }
+if ${ac_cv_o_cloexec+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <fcntl.h>
+int
+main ()
+{
+
+return open ("/dev/null", O_RDONLY | O_CLOEXEC);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_o_cloexec=yes
+else
+  ac_cv_o_cloexec=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_o_cloexec" >&5
+$as_echo "$ac_cv_o_cloexec" >&6; }
+if test $ac_cv_o_cloexec = yes; then
+
+$as_echo "#define HOST_HAS_O_CLOEXEC 1" >>confdefs.h
+
+fi
+
+# C++ Modules would like some networking features to provide the mapping
+# server.  You can still use modules without them though.
+# The following network-related checks could probably do with some
+# Windows and other non-linux defenses and checking.
+
+# Local socket connectivity wants AF_UNIX networking
+# Check for AF_UNIX networking
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for AF_UNIX" >&5
+$as_echo_n "checking for AF_UNIX... " >&6; }
+if ${ac_cv_af_unix+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+int
+main ()
+{
+
+sockaddr_un un;
+un.sun_family = AF_UNSPEC;
+int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&un, sizeof (un));
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_af_unix=yes
+else
+  ac_cv_af_unix=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_af_unix" >&5
+$as_echo "$ac_cv_af_unix" >&6; }
+if test $ac_cv_af_unix = yes; then
+
+$as_echo "#define HAVE_AF_UNIX 1" >>confdefs.h
+
+fi
+
+# Remote socket connectivity wants AF_INET6 networking
+# Check for AF_INET6 networking
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for AF_INET6" >&5
+$as_echo_n "checking for AF_INET6... " >&6; }
+if ${ac_cv_af_inet6+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+int
+main ()
+{
+
+sockaddr_in6 in6;
+in6.sin6_family = AF_UNSPEC;
+struct addrinfo *addrs = 0;
+struct addrinfo hints;
+hints.ai_flags = 0;
+hints.ai_family = AF_INET6;
+hints.ai_socktype = SOCK_STREAM;
+hints.ai_protocol = 0;
+hints.ai_canonname = 0;
+hints.ai_addr = 0;
+hints.ai_next = 0;
+int e = getaddrinfo ("localhost", 0, &hints, &addrs);
+const char *str = gai_strerror (e);
+freeaddrinfo (addrs);
+int fd = socket (AF_INET6, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&in6, sizeof (in6));
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_af_inet6=yes
+else
+  ac_cv_af_inet6=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_af_inet6" >&5
+$as_echo "$ac_cv_af_inet6" >&6; }
+if test $ac_cv_af_inet6 = yes; then
+
+$as_echo "#define HAVE_AF_INET6 1" >>confdefs.h
+
+fi
+
+# Check if _LK_LOCK is supported by _locking
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LK_LOCK" >&5
+$as_echo_n "checking for _LK_LOCK... " >&6; }
+if ${ac_cv_lk_lock+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <io.h>
+#include <sys/locking.h>
+int
+main ()
+{
+
+  int fd;
+  return _locking (fd, _LK_LOCK, 0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_lk_lock=yes
+else
+  ac_cv_lk_lock=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lk_lock" >&5
+$as_echo "$ac_cv_lk_lock" >&6; }
+if test $ac_cv_lk_lock = yes; then
+
+$as_echo "#define HOST_HAS_LK_LOCK 1" >>confdefs.h
+
+fi
+
 # Restore CFLAGS, CXXFLAGS from before the gcc_AC_NEED_DECLARATIONS tests.
 CFLAGS="$saved_CFLAGS"
 CXXFLAGS="$saved_CXXFLAGS"
@@ -19053,7 +19226,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 19056 "configure"
+#line 19229 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -19159,7 +19332,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 19162 "configure"
+#line 19335 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
diff --git a/gcc/configure.ac b/gcc/configure.ac
index c3efabc2e5e..c10d9e4240d 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -1244,7 +1244,7 @@ AC_HEADER_SYS_WAIT
 AC_HEADER_TIOCGWINSZ
 AC_CHECK_HEADERS(limits.h stddef.h string.h strings.h stdlib.h time.h iconv.h \
 		 fcntl.h ftw.h unistd.h sys/file.h sys/time.h sys/mman.h \
-		 sys/resource.h sys/param.h sys/times.h sys/stat.h \
+		 sys/resource.h sys/param.h sys/times.h sys/stat.h sys/locking.h \
 		 direct.h malloc.h langinfo.h ldfcn.h locale.h wchar.h)
 
 # Check for thread headers.
@@ -1617,6 +1617,85 @@ if test $ac_cv_f_setlkw = yes; then
   [Define if F_SETLKW supported by fcntl.])
 fi
 
+# Check if O_CLOEXEC is defined by fcntl
+AC_CACHE_CHECK(for O_CLOEXEC, ac_cv_o_cloexec, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <fcntl.h>]], [[
+return open ("/dev/null", O_RDONLY | O_CLOEXEC);]])],
+[ac_cv_o_cloexec=yes],[ac_cv_o_cloexec=no])])
+if test $ac_cv_o_cloexec = yes; then
+  AC_DEFINE(HOST_HAS_O_CLOEXEC, 1,
+  [Define if O_CLOEXEC supported by fcntl.])
+fi
+
+# C++ Modules would like some networking features to provide the mapping
+# server.  You can still use modules without them though.
+# The following network-related checks could probably do with some
+# Windows and other non-linux defenses and checking.
+
+# Local socket connectivity wants AF_UNIX networking
+# Check for AF_UNIX networking
+AC_CACHE_CHECK(for AF_UNIX, ac_cv_af_unix, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>]],[[
+sockaddr_un un;
+un.sun_family = AF_UNSPEC;
+int fd = socket (AF_UNIX, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&un, sizeof (un));]])],
+[ac_cv_af_unix=yes],
+[ac_cv_af_unix=no])])
+if test $ac_cv_af_unix = yes; then
+  AC_DEFINE(HAVE_AF_UNIX, 1,
+  [Define if AF_UNIX supported.])
+fi
+
+# Remote socket connectivity wants AF_INET6 networking
+# Check for AF_INET6 networking
+AC_CACHE_CHECK(for AF_INET6, ac_cv_af_inet6, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>]],[[
+sockaddr_in6 in6;
+in6.sin6_family = AF_UNSPEC;
+struct addrinfo *addrs = 0;
+struct addrinfo hints;
+hints.ai_flags = 0;
+hints.ai_family = AF_INET6;
+hints.ai_socktype = SOCK_STREAM;
+hints.ai_protocol = 0;
+hints.ai_canonname = 0;
+hints.ai_addr = 0;
+hints.ai_next = 0;
+int e = getaddrinfo ("localhost", 0, &hints, &addrs);
+const char *str = gai_strerror (e);
+freeaddrinfo (addrs);
+int fd = socket (AF_INET6, SOCK_STREAM, 0);
+connect (fd, (sockaddr *)&in6, sizeof (in6));]])],
+[ac_cv_af_inet6=yes],
+[ac_cv_af_inet6=no])])
+if test $ac_cv_af_inet6 = yes; then
+  AC_DEFINE(HAVE_AF_INET6, 1,
+  [Define if AF_INET6 supported.])
+fi
+
+# Check if _LK_LOCK is supported by _locking
+AC_CACHE_CHECK(for _LK_LOCK, ac_cv_lk_lock, [
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <io.h>
+#include <sys/locking.h>]], [[
+  int fd;
+  return _locking (fd, _LK_LOCK, 0);]])],
+[ac_cv_lk_lock=yes],[ac_cv_lk_lock=no])])
+if test $ac_cv_lk_lock = yes; then
+  AC_DEFINE(HOST_HAS_LK_LOCK, 1,
+  [Define if _LK_LOC supported by _locking.])
+fi
+
 # Restore CFLAGS, CXXFLAGS from before the gcc_AC_NEED_DECLARATIONS tests.
 CFLAGS="$saved_CFLAGS"
 CXXFLAGS="$saved_CXXFLAGS"
diff --git a/gcc/coverage.c b/gcc/coverage.c
index 2747f8f1b4c..6db29a40fb5 100644
--- a/gcc/coverage.c
+++ b/gcc/coverage.c
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "auto-profile.h"
 #include "profile.h"
 #include "diagnostic.h"
+#include "varasm.h"
 
 #include "gcov-io.c"
 
@@ -128,24 +129,14 @@ static const char *const ctr_names[GCOV_COUNTERS] = {
 #undef DEF_GCOV_COUNTER
 
 /* Forward declarations.  */
-static void read_counts_file (void);
 static tree build_var (tree, tree, int);
-static void build_fn_info_type (tree, unsigned, tree);
-static void build_info_type (tree, tree);
-static tree build_fn_info (const struct coverage_data *, tree, tree);
-static tree build_info (tree, tree);
-static bool coverage_obj_init (void);
-static vec<constructor_elt, va_gc> *coverage_obj_fn
-(vec<constructor_elt, va_gc> *, tree, struct coverage_data const *);
-static void coverage_obj_finish (vec<constructor_elt, va_gc> *);
 
 /* Return the type node for gcov_type.  */
 
 tree
 get_gcov_type (void)
 {
-  scalar_int_mode mode
-    = smallest_int_mode_for_size (LONG_LONG_TYPE_SIZE > 32 ? 64 : 32);
+  scalar_int_mode mode = smallest_int_mode_for_size (GCOV_TYPE_SIZE);
   return lang_hooks.types.type_for_mode (mode, false);
 }
 
@@ -217,6 +208,9 @@ read_counts_file (void)
   tag = gcov_read_unsigned ();
   bbg_file_stamp = crc32_unsigned (bbg_file_stamp, tag);
 
+  /* Read checksum.  */
+  gcov_read_unsigned ();
+
   counts_hash = new hash_table<counts_entry> (10);
   while ((tag = gcov_read_unsigned ()))
     {
@@ -245,7 +239,9 @@ read_counts_file (void)
       else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
 	{
 	  counts_entry **slot, *entry, elt;
-	  unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+	  int read_length = (int)length;
+	  length = read_length > 0 ? read_length : 0;
+	  unsigned n_counts = GCOV_TAG_COUNTER_NUM (abs (read_length));
 	  unsigned ix;
 
 	  elt.ident = fn_ident;
@@ -274,8 +270,9 @@ read_counts_file (void)
 	      counts_hash = NULL;
 	      break;
 	    }
-	  for (ix = 0; ix != n_counts; ix++)
-	    entry->counts[ix] += gcov_read_counter ();
+	  if (read_length > 0)
+	    for (ix = 0; ix != n_counts; ix++)
+	      entry->counts[ix] = gcov_read_counter ();
 	}
       gcov_sync (offset, length);
       if ((is_error = gcov_is_error ()))
@@ -345,8 +342,11 @@ get_coverage_counts (unsigned counter, unsigned cfg_checksum,
 	 can do about it.  */
       return NULL;
     }
-  
-  if (entry->cfg_checksum != cfg_checksum || entry->n_counts != n_counts)
+
+  if (entry->cfg_checksum != cfg_checksum
+      || (counter != GCOV_COUNTER_V_INDIR
+	  && counter != GCOV_COUNTER_V_TOPN
+	  && entry->n_counts != n_counts))
     {
       static int warned = 0;
       bool warning_printed = false;
@@ -925,6 +925,12 @@ build_info_type (tree type, tree fn_info_ptr_type)
   DECL_CHAIN (field) = fields;
   fields = field;
 
+  /* Checksum.  */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+		      get_gcov_unsigned_t ());
+  DECL_CHAIN (field) = fields;
+  fields = field;
+
   /* Filename */
   field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
 		      build_pointer_type (build_qualified_type
@@ -967,7 +973,7 @@ build_info_type (tree type, tree fn_info_ptr_type)
    function info objects.  */
 
 static tree
-build_info (tree info_type, tree fn_ary)
+build_info (tree info_type, tree fn_ary, unsigned object_checksum)
 {
   tree info_fields = TYPE_FIELDS (info_type);
   tree merge_fn_type, n_funcs;
@@ -986,13 +992,19 @@ build_info (tree info_type, tree fn_ary)
   /* next -- NULL */
   CONSTRUCTOR_APPEND_ELT (v1, info_fields, null_pointer_node);
   info_fields = DECL_CHAIN (info_fields);
-  
+
   /* stamp */
   CONSTRUCTOR_APPEND_ELT (v1, info_fields,
 			  build_int_cstu (TREE_TYPE (info_fields),
 					  bbg_file_stamp));
   info_fields = DECL_CHAIN (info_fields);
 
+  /* Checksum.  */
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+			  build_int_cstu (TREE_TYPE (info_fields),
+					  object_checksum));
+  info_fields = DECL_CHAIN (info_fields);
+
   /* Filename */
   da_file_name_len = strlen (da_file_name);
   filename_string = build_string (da_file_name_len + 1, da_file_name);
@@ -1097,6 +1109,26 @@ build_gcov_exit_decl (void)
   cgraph_build_static_cdtor ('D', dtor, priority);
 }
 
+/* Generate the pointer to the gcov_info_var in a dedicated section.  */
+
+static void
+build_gcov_info_var_registration (tree gcov_info_type)
+{
+  tree var = build_decl (BUILTINS_LOCATION,
+			 VAR_DECL, NULL_TREE,
+			 build_pointer_type (gcov_info_type));
+  TREE_STATIC (var) = 1;
+  TREE_READONLY (var) = 1;
+  char name_buf[32];
+  ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 2);
+  DECL_NAME (var) = get_identifier (name_buf);
+  get_section (profile_info_section, SECTION_UNNAMED, NULL);
+  set_decl_section_name (var, profile_info_section);
+  mark_decl_referenced (var);
+  DECL_INITIAL (var) = build_fold_addr_expr (gcov_info_var);
+  varpool_node::finalize_decl (var);
+}
+
 /* Create the gcov_info types and object.  Generate the constructor
    function to call __gcov_init.  Does not generate the initializer
    for the object.  Returns TRUE if coverage data is being emitted.  */
@@ -1151,8 +1183,13 @@ coverage_obj_init (void)
   ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0);
   DECL_NAME (gcov_info_var) = get_identifier (name_buf);
 
-  build_init_ctor (gcov_info_type);
-  build_gcov_exit_decl ();
+  if (profile_info_section)
+    build_gcov_info_var_registration (gcov_info_type);
+  else
+    {
+      build_init_ctor (gcov_info_type);
+      build_gcov_exit_decl ();
+    }
 
   return true;
 }
@@ -1179,7 +1216,8 @@ coverage_obj_fn (vec<constructor_elt, va_gc> *ctor, tree fn,
    function objects from CTOR.  Generate the gcov_info initializer.  */
 
 static void
-coverage_obj_finish (vec<constructor_elt, va_gc> *ctor)
+coverage_obj_finish (vec<constructor_elt, va_gc> *ctor,
+		     unsigned object_checksum)
 {
   unsigned n_functions = vec_safe_length (ctor);
   tree fn_info_ary_type = build_array_type
@@ -1196,7 +1234,7 @@ coverage_obj_finish (vec<constructor_elt, va_gc> *ctor)
   varpool_node::finalize_decl (fn_info_ary);
   
   DECL_INITIAL (gcov_info_var)
-    = build_info (TREE_TYPE (gcov_info_var), fn_info_ary);
+    = build_info (TREE_TYPE (gcov_info_var), fn_info_ary, object_checksum);
   varpool_node::finalize_decl (gcov_info_var);
 }
 
@@ -1266,7 +1304,6 @@ coverage_init (const char *filename)
   strcpy (da_file_name + prefix_len + len, GCOV_DATA_SUFFIX);
 
   bbg_file_stamp = local_tick;
-  
   if (flag_auto_profile)
     read_autofdo_file ();
   else if (flag_branch_probabilities)
@@ -1294,6 +1331,8 @@ coverage_init (const char *filename)
 	  gcov_write_unsigned (GCOV_NOTE_MAGIC);
 	  gcov_write_unsigned (GCOV_VERSION);
 	  gcov_write_unsigned (bbg_file_stamp);
+	  /* Use an arbitrary checksum */
+	  gcov_write_unsigned (0);
 	  gcov_write_string (getpwd ());
 
 	  /* Do not support has_unexecuted_blocks for Ada.  */
@@ -1319,14 +1358,24 @@ coverage_finish (void)
        cannot uniquely stamp it.  If we can stamp it, libgcov will DTRT.  */
     unlink (da_file_name);
 
+  /* Global GCDA checksum that aggregates all functions.  */
+  unsigned object_checksum = 0;
+
   if (coverage_obj_init ())
     {
       vec<constructor_elt, va_gc> *fn_ctor = NULL;
       struct coverage_data *fn;
       
       for (fn = functions_head; fn; fn = fn->next)
-	fn_ctor = coverage_obj_fn (fn_ctor, fn->fn_decl, fn);
-      coverage_obj_finish (fn_ctor);
+	{
+	  fn_ctor = coverage_obj_fn (fn_ctor, fn->fn_decl, fn);
+
+	  object_checksum = crc32_unsigned (object_checksum, fn->ident);
+	  object_checksum = crc32_unsigned (object_checksum,
+					    fn->lineno_checksum);
+	  object_checksum = crc32_unsigned (object_checksum, fn->cfg_checksum);
+	}
+      coverage_obj_finish (fn_ctor, object_checksum);
     }
 
   XDELETEVEC (da_file_name);
diff --git a/gcc/dbgcnt.c b/gcc/dbgcnt.c
index b0dd893ed49..50b9aed07a0 100644
--- a/gcc/dbgcnt.c
+++ b/gcc/dbgcnt.c
@@ -194,7 +194,6 @@ void
 dbg_cnt_process_opt (const char *arg)
 {
   char *str = xstrdup (arg);
-  unsigned int start = 0;
 
   auto_vec<char *> tokens;
   for (char *next = strtok (str, ","); next != NULL; next = strtok (NULL, ","))
@@ -213,7 +212,6 @@ dbg_cnt_process_opt (const char *arg)
 	  if (!dbg_cnt_process_single_pair (name, ranges[j]))
 	    break;
 	}
-      start += strlen (tokens[i]) + 1;
     }
 }
 
diff --git a/gcc/defaults.h b/gcc/defaults.h
index f1a38626624..513ea896ec5 100644
--- a/gcc/defaults.h
+++ b/gcc/defaults.h
@@ -1473,4 +1473,12 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 typedef TARGET_UNIT target_unit;
 #endif
 
+#ifndef GCOV_TYPE_SIZE
+#if LONG_LONG_TYPE_SIZE > 32
+#define GCOV_TYPE_SIZE 64
+#else
+#define GCOV_TYPE_SIZE 32
+#endif
+#endif
+
 #endif  /* ! GCC_DEFAULTS_H */
diff --git a/gcc/doc/gcov-dump.texi b/gcc/doc/gcov-dump.texi
index 776c0d591a4..419df988df4 100644
--- a/gcc/doc/gcov-dump.texi
+++ b/gcc/doc/gcov-dump.texi
@@ -61,6 +61,7 @@ gcov-dump [@option{-v}|@option{--version}]
      [@option{-h}|@option{--help}]
      [@option{-l}|@option{--long}]
      [@option{-p}|@option{--positions}]
+     [@option{-r}|@option{--raw}]
      @var{gcovfiles}
 @c man end
 @end ignore
@@ -80,6 +81,10 @@ Dump content of records.
 @itemx --positions
 Dump positions of records.
 
+@item -r
+@itemx --raw
+Print content records in raw format.
+
 @item -v
 @itemx --version
 Display the @command{gcov-dump} version number (on the standard output),
diff --git a/gcc/doc/gcov-tool.texi b/gcc/doc/gcov-tool.texi
index 71e9b607cc4..1a63ad5d595 100644
--- a/gcc/doc/gcov-tool.texi
+++ b/gcc/doc/gcov-tool.texi
@@ -52,6 +52,10 @@ Current gcov-tool supports the following functionalities:
 @item
 merge two sets of profiles with weights.
 
+@item
+read a stream of profiles with associated filenames and merge it with a set of
+profiles with weights.
+
 @item
 read one set of profile and rewrite profile contents. One can scale or
 normalize the count values.
@@ -64,6 +68,12 @@ Collect the profiles for different set of inputs, and use this tool to merge
 them. One can specify the weight to factor in the relative importance of
 each input.
 
+@item
+Collect profiles from target systems without a filesystem (freestanding
+environments).  Merge the collected profiles with associated profiles
+present on the host system.  One can specify the weight to factor in the
+relative importance of each input.
+
 @item
 Rewrite the profile after removing a subset of the gcda files, while maintaining
 the consistency of the summary and the histogram.
@@ -117,6 +127,10 @@ gcov-tool merge [merge-options] @var{directory1} @var{directory2}
      [@option{-v}|@option{--verbose}]
      [@option{-w}|@option{--weight} @var{w1,w2}]
 
+gcov-tool merge-stream [merge-stream-options] [@var{file}]
+     [@option{-v}|@option{--verbose}]
+     [@option{-w}|@option{--weight} @var{w1,w2}]
+
 gcov-tool rewrite [rewrite-options] @var{directory}
      [@option{-n}|@option{--normalize} @var{long_long_value}]
      [@option{-o}|@option{--output} @var{directory}]
@@ -169,6 +183,28 @@ Set the merge weights of the @var{directory1} and @var{directory2},
 respectively. The default weights are 1 for both.
 @end table
 
+@item merge-stream
+Collect profiles with associated filenames from a @emph{gcfn} and @emph{gcda}
+data stream.  Read the stream from the file specified by @var{file} or from
+@file{stdin}.  Merge the profiles with associated profiles in the host
+filesystem.  Apply the optional weights while merging profiles.
+
+For the generation of a @emph{gcfn} and @emph{gcda} data stream on the target
+system, please have a look at the @code{__gcov_filename_to_gcfn()} and
+@code{__gcov_info_to_gcda()} functions declared in @code{#include <gcov.h>}.
+@table @gcctabopt
+
+@item -v
+@itemx --verbose
+Set the verbose mode.
+
+@item -w @var{w1},@var{w2}
+@itemx --weight @var{w1},@var{w2}
+Set the merge weights of the profiles from the @emph{gcfn} and @emph{gcda} data
+stream and the associated profiles in the host filesystem, respectively.  The
+default weights are 1 for both.
+@end table
+
 @item rewrite
 Read the specified profile directory and rewrite to a new directory.
 @table @gcctabopt
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d7c787af226..08192ff5186 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -549,6 +549,7 @@ Objective-C and Objective-C++ Dialects}.
 @gccoptlist{-p  -pg  -fprofile-arcs  --coverage  -ftest-coverage @gol
 -fprofile-abs-path @gol
 -fprofile-dir=@var{path}  -fprofile-generate  -fprofile-generate=@var{path} @gol
+-fprofile-info-section  -fprofile-info-section=@var{name} @gol
 -fprofile-note=@var{path} -fprofile-prefix-path=@var{path} @gol
 -fprofile-update=@var{method} -fprofile-filter-files=@var{regex} @gol
 -fprofile-exclude-files=@var{regex} @gol
@@ -13355,6 +13356,10 @@ supports locking, the data files will be correctly updated.  Unless
 a strict ISO C dialect option is in effect, @code{fork} calls are
 detected and correctly handled without double counting.
 
+Moreover, an object file can be recompiled multiple times
+and the corresponding @file{.gcda} file merges as long as
+the source file and the compiler options are unchanged.
+
 @item
 For profile-directed optimizations, compile the source files again with
 the same optimization and code generation options plus
@@ -13442,6 +13447,97 @@ the profile feedback data files. See @option{-fprofile-dir}.
 To optimize the program based on the collected profile information, use
 @option{-fprofile-use}.  @xref{Optimize Options}, for more information.
 
+@item -fprofile-info-section
+@itemx -fprofile-info-section=@var{name}
+@opindex fprofile-info-section
+
+Register the profile information in the specified section instead of using a
+constructor/destructor.  The section name is @var{name} if it is specified,
+otherwise the section name defaults to @code{.gcov_info}.  A pointer to the
+profile information generated by @option{-fprofile-arcs} is placed in the
+specified section for each translation unit.  This option disables the profile
+information registration through a constructor and it disables the profile
+information processing through a destructor.  This option is not intended to be
+used in hosted environments such as GNU/Linux.  It targets freestanding
+environments (for example embedded systems) with limited resources which do not
+support constructors/destructors or the C library file I/O.
+
+The linker could collect the input sections in a continuous memory block and
+define start and end symbols.  A GNU linker script example which defines a
+linker output section follows:
+
+@smallexample
+  .gcov_info      :
+  @{
+    PROVIDE (__gcov_info_start = .);
+    KEEP (*(.gcov_info))
+    PROVIDE (__gcov_info_end = .);
+  @}
+@end smallexample
+
+The program could dump the profiling information registered in this linker set
+for example like this:
+
+@smallexample
+#include <gcov.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern const struct gcov_info *const __gcov_info_start[];
+extern const struct gcov_info *const __gcov_info_end[];
+
+static void
+dump (const void *d, unsigned n, void *arg)
+@{
+  const unsigned char *c = d;
+
+  for (unsigned i = 0; i < n; ++i)
+    printf ("%02x", c[i]);
+@}
+
+static void
+filename (const char *f, void *arg)
+@{
+  __gcov_filename_to_gcfn (f, dump, arg );
+@}
+
+static void *
+allocate (unsigned length, void *arg)
+@{
+  return malloc (length);
+@}
+
+static void
+dump_gcov_info (void)
+@{
+  const struct gcov_info *const *info = __gcov_info_start;
+  const struct gcov_info *const *end = __gcov_info_end;
+
+  /* Obfuscate variable to prevent compiler optimizations.  */
+  __asm__ ("" : "+r" (info));
+
+  while (info != end)
+  @{
+    void *arg = NULL;
+    __gcov_info_to_gcda (*info, filename, dump, allocate, arg);
+    putchar ('\n');
+    ++info;
+  @}
+@}
+
+int
+main (void)
+@{
+  dump_gcov_info ();
+  return 0;
+@}
+@end smallexample
+
+The @command{merge-stream} subcommand of @command{gcov-tool} may be used to
+deserialize the data stream generated by the @code{__gcov_filename_to_gcfn} and
+@code{__gcov_info_to_gcda} functions and merge the profile information into
+@file{.gcda} files on the host filesystem.
+
 @item -fprofile-note=@var{path}
 @opindex fprofile-note
 
diff --git a/gcc/gcov-dump.c b/gcc/gcov-dump.c
index bd2ae223ac6..0a2b108fe6b 100644
--- a/gcc/gcov-dump.c
+++ b/gcc/gcov-dump.c
@@ -32,23 +32,24 @@ static void dump_gcov_file (const char *);
 static void print_prefix (const char *, unsigned, gcov_position_t);
 static void print_usage (void);
 static void print_version (void);
-static void tag_function (const char *, unsigned, unsigned, unsigned);
-static void tag_blocks (const char *, unsigned, unsigned, unsigned);
-static void tag_arcs (const char *, unsigned, unsigned, unsigned);
-static void tag_lines (const char *, unsigned, unsigned, unsigned);
-static void tag_counters (const char *, unsigned, unsigned, unsigned);
-static void tag_summary (const char *, unsigned, unsigned, unsigned);
+static void tag_function (const char *, unsigned, int, unsigned);
+static void tag_blocks (const char *, unsigned, int, unsigned);
+static void tag_arcs (const char *, unsigned, int, unsigned);
+static void tag_lines (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 **);
 
 typedef struct tag_format
 {
   unsigned tag;
   char const *name;
-  void (*proc) (const char *, unsigned, unsigned, unsigned);
+  void (*proc) (const char *, unsigned, int, unsigned);
 } tag_format_t;
 
 static int flag_dump_contents = 0;
 static int flag_dump_positions = 0;
+static int flag_dump_raw = 0;
 
 static const struct option options[] =
 {
@@ -95,7 +96,7 @@ main (int argc ATTRIBUTE_UNUSED, char **argv)
 
   diagnostic_initialize (global_dc, 0);
 
-  while ((opt = getopt_long (argc, argv, "hlpvw", options, NULL)) != -1)
+  while ((opt = getopt_long (argc, argv, "hlprvw", options, NULL)) != -1)
     {
       switch (opt)
 	{
@@ -111,6 +112,9 @@ main (int argc ATTRIBUTE_UNUSED, char **argv)
 	case 'p':
 	  flag_dump_positions = 1;
 	  break;
+	case 'r':
+	  flag_dump_raw = 1;
+	  break;
 	default:
 	  fprintf (stderr, "unknown flag `%c'\n", opt);
 	}
@@ -129,6 +133,7 @@ print_usage (void)
   printf ("  -h, --help           Print this help\n");
   printf ("  -l, --long           Dump record contents too\n");
   printf ("  -p, --positions      Dump record positions\n");
+  printf ("  -r, --raw		  Print content records in raw format\n");
   printf ("  -v, --version        Print version number\n");
   printf ("\nFor bug reporting instructions, please see:\n%s.\n",
 	   bug_report_url);
@@ -202,11 +207,12 @@ dump_gcov_file (const char *filename)
   }
 
   /* stamp */
-  {
-    unsigned stamp = gcov_read_unsigned ();
+  unsigned stamp = gcov_read_unsigned ();
+  printf ("%s:stamp %lu\n", filename, (unsigned long)stamp);
 
-    printf ("%s:stamp %lu\n", filename, (unsigned long)stamp);
-  }
+  /* Checksum */
+  unsigned checksum = gcov_read_unsigned ();
+  printf ("%s:checksum %lu\n", filename, (unsigned long)checksum);
 
   if (!is_data_type)
     {
@@ -221,6 +227,7 @@ dump_gcov_file (const char *filename)
   while (1)
     {
       gcov_position_t base, position = gcov_position ();
+      int read_length;
       unsigned tag, length;
       tag_format_t const *format;
       unsigned tag_depth;
@@ -230,7 +237,8 @@ dump_gcov_file (const char *filename)
       tag = gcov_read_unsigned ();
       if (!tag)
 	break;
-      length = gcov_read_unsigned ();
+      read_length = (int)gcov_read_unsigned ();
+      length = read_length > 0 ? read_length : 0;
       base = gcov_position ();
       mask = GCOV_TAG_MASK (tag) >> 1;
       for (tag_depth = 4; mask; mask >>= 8)
@@ -260,9 +268,9 @@ dump_gcov_file (const char *filename)
 	}
 
       print_prefix (filename, tag_depth, position);
-      printf ("%08x:%4u:%s", tag, length, format->name);
+      printf ("%08x:%4u:%s", tag, abs (read_length), format->name);
       if (format->proc)
-	(*format->proc) (filename, tag, length, depth);
+	(*format->proc) (filename, tag, read_length, depth);
 
       printf ("\n");
       if (flag_dump_contents && format->proc)
@@ -290,10 +298,10 @@ dump_gcov_file (const char *filename)
 
 static void
 tag_function (const char *filename ATTRIBUTE_UNUSED,
-	      unsigned tag ATTRIBUTE_UNUSED, unsigned length,
+	      unsigned tag ATTRIBUTE_UNUSED, int length,
 	      unsigned depth ATTRIBUTE_UNUSED)
 {
-  unsigned long pos = gcov_position ();
+  long pos = gcov_position ();
 
   if (!length)
     printf (" placeholder");
@@ -326,7 +334,7 @@ tag_function (const char *filename ATTRIBUTE_UNUSED,
 
 static void
 tag_blocks (const char *filename ATTRIBUTE_UNUSED,
-	    unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+	    unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
 	    unsigned depth ATTRIBUTE_UNUSED)
 {
   printf (" %u blocks", gcov_read_unsigned ());
@@ -334,7 +342,7 @@ tag_blocks (const char *filename ATTRIBUTE_UNUSED,
 
 static void
 tag_arcs (const char *filename ATTRIBUTE_UNUSED,
-	  unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+	  unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
 	  unsigned depth)
 {
   unsigned n_arcs = GCOV_TAG_ARCS_NUM (length);
@@ -376,7 +384,7 @@ tag_arcs (const char *filename ATTRIBUTE_UNUSED,
 
 static void
 tag_lines (const char *filename ATTRIBUTE_UNUSED,
-	   unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+	   unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
 	   unsigned depth)
 {
   if (flag_dump_contents)
@@ -421,7 +429,7 @@ tag_lines (const char *filename ATTRIBUTE_UNUSED,
 
 static void
 tag_counters (const char *filename ATTRIBUTE_UNUSED,
-	      unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+	      unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
 	      unsigned depth)
 {
 #define DEF_GCOV_COUNTER(COUNTER, NAME, MERGE_FN) NAME,
@@ -429,26 +437,32 @@ tag_counters (const char *filename ATTRIBUTE_UNUSED,
 #include "gcov-counter.def"
 };
 #undef DEF_GCOV_COUNTER
-  unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+  int n_counts = GCOV_TAG_COUNTER_NUM (length);
+  bool has_zeros = n_counts < 0;
+  n_counts = abs (n_counts);
 
-  printf (" %s %u counts",
-	  counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts);
+  printf (" %s %u counts%s",
+	  counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts,
+	  has_zeros ? " (all zero)" : "");
   if (flag_dump_contents)
     {
-      unsigned ix;
-
-      for (ix = 0; ix != n_counts; ix++)
+      for (int ix = 0; ix != n_counts; ix++)
 	{
 	  gcov_type count;
 
-	  if (!(ix & 7))
+	  if (flag_dump_raw)
+	    {
+	      if (ix == 0)
+		printf (": ");
+	    }
+	  else if (!(ix & 7))
 	    {
 	      printf ("\n");
 	      print_prefix (filename, depth, gcov_position ());
 	      printf (VALUE_PADDING_PREFIX VALUE_PREFIX, ix);
 	    }
 
-	  count = gcov_read_counter ();
+	  count = has_zeros ? 0 : gcov_read_counter ();
 	  printf ("%" PRId64 " ", count);
 	}
     }
@@ -456,7 +470,7 @@ tag_counters (const char *filename ATTRIBUTE_UNUSED,
 
 static void
 tag_summary (const char *filename ATTRIBUTE_UNUSED,
-	     unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED,
+	     unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED,
 	     unsigned depth ATTRIBUTE_UNUSED)
 {
   gcov_summary summary;
diff --git a/gcc/gcov-io.c b/gcc/gcov-io.c
index ac60f9baf47..a6ec164efb2 100644
--- a/gcc/gcov-io.c
+++ b/gcc/gcov-io.c
@@ -27,42 +27,31 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 /* Routines declared in gcov-io.h.  This file should be #included by
    another source file, after having #included gcov-io.h.  */
 
-#if !IN_GCOV
-static void gcov_write_block (unsigned);
-static gcov_unsigned_t *gcov_write_words (unsigned);
-#endif
-static const gcov_unsigned_t *gcov_read_words (unsigned);
-#if !IN_LIBGCOV
-static void gcov_allocate (unsigned);
-#endif
+static gcov_unsigned_t *gcov_read_words (void *buffer, unsigned);
 
-/* Optimum number of gcov_unsigned_t's read from or written to disk.  */
-#define GCOV_BLOCK_SIZE (1 << 10)
+/* Indicates the last gcov file access error or that no error occurred
+   so far.  */
+enum gcov_file_error
+{
+  GCOV_FILE_COUNTER_OVERFLOW = -1,
+  GCOV_FILE_NO_ERROR = 0,
+  GCOV_FILE_WRITE_ERROR = 1,
+  GCOV_FILE_EOF = 2
+};
 
 struct gcov_var
 {
   FILE *file;
-  gcov_position_t start;	/* Position of first byte of block */
-  unsigned offset;		/* Read/write position within the block.  */
-  unsigned length;		/* Read limit in the block.  */
-  unsigned overread;		/* Number of words overread.  */
-  int error;			/* < 0 overflow, > 0 disk error.  */
-  int mode;	                /* < 0 writing, > 0 reading */
+  enum gcov_file_error error;
+  int mode;			/* < 0 writing, > 0 reading.  */
   int endian;			/* Swap endianness.  */
-#if IN_LIBGCOV
-  /* Holds one block plus 4 bytes, thus all coverage reads & writes
-     fit within this buffer and we always can transfer GCOV_BLOCK_SIZE
-     to and from the disk. libgcov never backtracks and only writes 4
-     or 8 byte objects.  */
-  gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1];
-#else
-  /* Holds a variable length block, as the compiler can write
-     strings and needs to backtrack.  */
-  size_t alloc;
-  gcov_unsigned_t *buffer;
+#ifdef IN_GCOV_TOOL
+  gcov_position_t pos;		/* File position for stdin support.  */
 #endif
 } gcov_var;
 
+#define GCOV_MODE_STDIN 2
+
 /* Save the current position in the gcov file.  */
 /* We need to expose this function when compiling for gcov-tool.  */
 #ifndef IN_GCOV_TOOL
@@ -71,8 +60,11 @@ static inline
 gcov_position_t
 gcov_position (void)
 {
-  gcov_nonruntime_assert (gcov_var.mode > 0); 
-  return gcov_var.start + gcov_var.offset;
+#ifdef IN_GCOV_TOOL
+  if (gcov_var.mode == GCOV_MODE_STDIN)
+    return gcov_var.pos;
+#endif
+  return ftell (gcov_var.file);
 }
 
 /* Return nonzero if the error flag is set.  */
@@ -87,25 +79,24 @@ gcov_is_error (void)
 }
 
 #if IN_LIBGCOV
-/* Move to beginning of file and initialize for writing.  */
+/* Move to beginning of file, initialize for writing, and clear file error
+   status.  */
+
 GCOV_LINKAGE inline void
 gcov_rewrite (void)
 {
   gcov_var.mode = -1; 
-  gcov_var.start = 0;
-  gcov_var.offset = 0;
+  gcov_var.error = GCOV_FILE_NO_ERROR;
   fseek (gcov_var.file, 0L, SEEK_SET);
 }
 #endif
 
-static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
+static inline gcov_unsigned_t
+from_file (gcov_unsigned_t value)
 {
 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
   if (gcov_var.endian)
-    {
-      value = (value >> 16) | (value << 16);
-      value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
-    }
+    return __builtin_bswap32 (value);
 #endif
   return value;
 }
@@ -120,15 +111,8 @@ static inline gcov_unsigned_t from_file (gcov_unsigned_t value)
    Return zero on failure, non-zero on success.  */
 
 GCOV_LINKAGE int
-#if IN_LIBGCOV
-gcov_open (const char *name)
-#else
 gcov_open (const char *name, int mode)
-#endif
 {
-#if IN_LIBGCOV
-  int mode = 0;
-#endif
 #if GCOV_LOCKED
   struct flock s_flock;
   int fd;
@@ -137,16 +121,25 @@ gcov_open (const char *name, int mode)
   s_flock.l_start = 0;
   s_flock.l_len = 0; /* Until EOF.  */
   s_flock.l_pid = getpid ();
+#elif GCOV_LOCKED_WITH_LOCKING
+  int fd;
 #endif
 
   gcov_nonruntime_assert (!gcov_var.file);
-  gcov_var.start = 0;
-  gcov_var.offset = gcov_var.length = 0;
-  gcov_var.overread = -1u;
-  gcov_var.error = 0;
-#if !IN_LIBGCOV
+  gcov_var.error = GCOV_FILE_NO_ERROR;
+#if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
   gcov_var.endian = 0;
 #endif
+#ifdef IN_GCOV_TOOL
+  gcov_var.pos = 0;
+  if (!name)
+    {
+      gcov_nonruntime_assert (gcov_var.mode > 0);
+      gcov_var.file = stdin;
+      gcov_var.mode = GCOV_MODE_STDIN;
+      return 1;
+    }
+#endif
 #if GCOV_LOCKED
   if (mode > 0)
     {
@@ -170,6 +163,34 @@ gcov_open (const char *name, int mode)
 
   gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
 
+  if (!gcov_var.file)
+    {
+      close (fd);
+      return 0;
+    }
+#elif GCOV_LOCKED_WITH_LOCKING
+  if (mode > 0)
+    {
+      /* pass mode (ignored) for compatibility */
+      fd = open (name, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR);
+    }
+  else
+     {
+       /* Truncate if force new mode.  */
+       fd = open (name, O_RDWR | O_BINARY | O_CREAT | (mode < 0 ? O_TRUNC : 0),
+		  0666);
+    }
+  if (fd < 0)
+    return 0;
+
+  if (_locking (fd, _LK_LOCK, LONG_MAX) < 0)
+    {
+      close (fd);
+      return 0;
+    }
+
+  gcov_var.file = fdopen (fd, (mode > 0) ? "rb" : "r+b");
+
   if (!gcov_var.file)
     {
       close (fd);
@@ -192,8 +213,6 @@ gcov_open (const char *name, int mode)
 
   gcov_var.mode = mode ? mode : 1;
 
-  setbuf (gcov_var.file, (char *)0);
-
   return 1;
 }
 
@@ -203,21 +222,18 @@ gcov_open (const char *name, int mode)
 GCOV_LINKAGE int
 gcov_close (void)
 {
+#ifdef IN_GCOV_TOOL
+  if (gcov_var.file == stdin)
+    gcov_var.file = 0;
+  else
+#endif
   if (gcov_var.file)
     {
-#if !IN_GCOV
-      if (gcov_var.offset && gcov_var.mode < 0)
-	gcov_write_block (gcov_var.offset);
-#endif
-      fclose (gcov_var.file);
+      if (fclose (gcov_var.file))
+	gcov_var.error = GCOV_FILE_WRITE_ERROR;
+
       gcov_var.file = 0;
-      gcov_var.length = 0;
     }
-#if !IN_LIBGCOV
-  free (gcov_var.buffer);
-  gcov_var.alloc = 0;
-  gcov_var.buffer = 0;
-#endif
   gcov_var.mode = 0;
   return gcov_var.error;
 }
@@ -232,9 +248,8 @@ gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
 {
   if (magic == expected)
     return 1;
-  magic = (magic >> 16) | (magic << 16);
-  magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
-  if (magic == expected)
+
+  if (__builtin_bswap32 (magic) == expected)
     {
       gcov_var.endian = 1;
       return -1;
@@ -243,60 +258,15 @@ gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected)
 }
 #endif
 
-#if !IN_LIBGCOV
-static void
-gcov_allocate (unsigned length)
-{
-  size_t new_size = gcov_var.alloc;
-
-  if (!new_size)
-    new_size = GCOV_BLOCK_SIZE;
-  new_size += length;
-  new_size *= 2;
-
-  gcov_var.alloc = new_size;
-  gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2);
-}
-#endif
-
 #if !IN_GCOV
-/* Write out the current block, if needs be.  */
+/* Write DATA of LENGTH characters to coverage file.  */
 
-static void
-gcov_write_block (unsigned size)
-{
-  if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1)
-    gcov_var.error = 1;
-  gcov_var.start += size;
-  gcov_var.offset -= size;
-}
-
-/* Allocate space to write BYTES bytes to the gcov file. Return a
-   pointer to those bytes, or NULL on failure.  */
-
-static gcov_unsigned_t *
-gcov_write_words (unsigned words)
+GCOV_LINKAGE void
+gcov_write (const void *data, unsigned length)
 {
-  gcov_unsigned_t *result;
-
-  gcov_nonruntime_assert (gcov_var.mode < 0);
-#if IN_LIBGCOV
-  if (gcov_var.offset >= GCOV_BLOCK_SIZE)
-    {
-      gcov_write_block (GCOV_BLOCK_SIZE);
-      if (gcov_var.offset)
-	{
-	  memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
-	}
-    }
-#else
-  if (gcov_var.offset + words > gcov_var.alloc)
-    gcov_allocate (gcov_var.offset + words);
-#endif
-  result = &gcov_var.buffer[gcov_var.offset];
-  gcov_var.offset += words;
-
-  return result;
+  gcov_unsigned_t r = fwrite (data, length, 1, gcov_var.file);
+  if (r != 1)
+    gcov_var.error = GCOV_FILE_WRITE_ERROR;
 }
 
 /* Write unsigned VALUE to coverage file.  Sets error flag
@@ -305,9 +275,9 @@ gcov_write_words (unsigned words)
 GCOV_LINKAGE void
 gcov_write_unsigned (gcov_unsigned_t value)
 {
-  gcov_unsigned_t *buffer = gcov_write_words (1);
-
-  buffer[0] = value;
+  gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file);
+  if (r != 1)
+    gcov_var.error = GCOV_FILE_WRITE_ERROR;
 }
 
 /* Write counter VALUE to coverage file.  Sets error flag
@@ -317,13 +287,11 @@ gcov_write_unsigned (gcov_unsigned_t value)
 GCOV_LINKAGE void
 gcov_write_counter (gcov_type value)
 {
-  gcov_unsigned_t *buffer = gcov_write_words (2);
-
-  buffer[0] = (gcov_unsigned_t) value;
+  gcov_write_unsigned ((gcov_unsigned_t) value);
   if (sizeof (value) > sizeof (gcov_unsigned_t))
-    buffer[1] = (gcov_unsigned_t) (value >> 32);
+    gcov_write_unsigned ((gcov_unsigned_t) (value >> 32));
   else
-    buffer[1] = 0;
+    gcov_write_unsigned (0);
 }
 #endif /* IN_LIBGCOV */
 
@@ -335,23 +303,16 @@ GCOV_LINKAGE void
 gcov_write_string (const char *string)
 {
   unsigned length = 0;
-  unsigned alloc = 0;
-  gcov_unsigned_t *buffer;
 
   if (string)
-    {
-      length = strlen (string);
-      alloc = (length + 4) >> 2;
-    }
-
-  buffer = gcov_write_words (1 + alloc);
-
-  buffer[0] = alloc;
+    length = strlen (string) + 1;
 
-  if (alloc > 0)
+  gcov_write_unsigned (length);
+  if (length > 0)
     {
-      buffer[alloc] = 0; /* place nul terminators.  */
-      memcpy (&buffer[1], string, length);
+      gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file);
+      if (r != 1)
+	gcov_var.error = GCOV_FILE_WRITE_ERROR;
     }
 }
 #endif
@@ -386,20 +347,24 @@ gcov_write_filename (const char *filename)
 
   gcov_write_string (filename);
 }
-#endif
 
-#if !IN_LIBGCOV
+/* Move to a given position in a gcov file.  */
+
+static void
+gcov_seek (gcov_position_t base)
+{
+  fseek (gcov_var.file, base, SEEK_SET);
+}
+
 /* Write a tag TAG and reserve space for the record length. Return a
    value to be used for gcov_write_length.  */
 
 GCOV_LINKAGE gcov_position_t
 gcov_write_tag (gcov_unsigned_t tag)
 {
-  gcov_position_t result = gcov_var.start + gcov_var.offset;
-  gcov_unsigned_t *buffer = gcov_write_words (2);
-
-  buffer[0] = tag;
-  buffer[1] = 0;
+  gcov_position_t result = gcov_position ();
+  gcov_write_unsigned (tag);
+  gcov_write_unsigned (0);
 
   return result;
 }
@@ -412,19 +377,13 @@ gcov_write_tag (gcov_unsigned_t tag)
 GCOV_LINKAGE void
 gcov_write_length (gcov_position_t position)
 {
-  unsigned offset;
-  gcov_unsigned_t length;
-  gcov_unsigned_t *buffer;
-
+  gcov_position_t current_position = gcov_position ();
   gcov_nonruntime_assert (gcov_var.mode < 0);
-  gcov_nonruntime_assert (position + 2 <= gcov_var.start + gcov_var.offset);
-  gcov_nonruntime_assert (position >= gcov_var.start);
-  offset = position - gcov_var.start;
-  length = gcov_var.offset - offset - 2;
-  buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset];
-  buffer[1] = length;
-  if (gcov_var.offset >= GCOV_BLOCK_SIZE)
-    gcov_write_block (gcov_var.offset);
+  gcov_nonruntime_assert (current_position >= position + 2 * GCOV_WORD_SIZE);
+
+  gcov_seek (position + GCOV_WORD_SIZE);
+  gcov_write_unsigned (current_position - position - 2 * GCOV_WORD_SIZE);
+  gcov_seek (current_position);
 }
 
 #else /* IN_LIBGCOV */
@@ -434,10 +393,8 @@ gcov_write_length (gcov_position_t position)
 GCOV_LINKAGE void
 gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
 {
-  gcov_unsigned_t *buffer = gcov_write_words (2);
-
-  buffer[0] = tag;
-  buffer[1] = length;
+  gcov_write_unsigned (tag);
+  gcov_write_unsigned (length);
 }
 
 /* Write a summary structure to the gcov file.  Return nonzero on
@@ -446,7 +403,8 @@ gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length)
 GCOV_LINKAGE void
 gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
 {
-  gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH);
+  gcov_write_unsigned (tag);
+  gcov_write_unsigned (GCOV_TAG_SUMMARY_LENGTH);
   gcov_write_unsigned (summary->runs);
   gcov_write_unsigned (summary->sum_max);
 }
@@ -455,52 +413,35 @@ gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary)
 
 #endif /*!IN_GCOV */
 
-/* Return a pointer to read BYTES bytes from the gcov file. Returns
+/* Return a pointer to read COUNT bytes from the gcov file.  Returns
    NULL on failure (read past EOF).  */
 
-static const gcov_unsigned_t *
-gcov_read_words (unsigned words)
+static void *
+gcov_read_bytes (void *buffer, unsigned count)
 {
-  const gcov_unsigned_t *result;
-  unsigned excess = gcov_var.length - gcov_var.offset;
-
   if (gcov_var.mode <= 0)
     return NULL;
 
-  if (excess < words)
+  unsigned read = fread (buffer, count, 1, gcov_var.file);
+  if (read != 1)
     {
-      gcov_var.start += gcov_var.offset;
-      if (excess)
-	{
-#if IN_LIBGCOV
-	  memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4);
-#else
-	  memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset,
-		   excess * 4);
-#endif
-	}
-      gcov_var.offset = 0;
-      gcov_var.length = excess;
-#if IN_LIBGCOV
-      excess = GCOV_BLOCK_SIZE;
-#else
-      if (gcov_var.length + words > gcov_var.alloc)
-	gcov_allocate (gcov_var.length + words);
-      excess = gcov_var.alloc - gcov_var.length;
-#endif
-      excess = fread (gcov_var.buffer + gcov_var.length,
-		      1, excess << 2, gcov_var.file) >> 2;
-      gcov_var.length += excess;
-      if (gcov_var.length < words)
-	{
-	  gcov_var.overread += words - gcov_var.length;
-	  gcov_var.length = 0;
-	  return 0;
-	}
+      if (feof (gcov_var.file))
+	gcov_var.error = GCOV_FILE_EOF;
+      return NULL;
     }
-  result = &gcov_var.buffer[gcov_var.offset];
-  gcov_var.offset += words;
-  return result;
+
+#ifdef IN_GCOV_TOOL
+  gcov_var.pos += count;
+#endif
+  return buffer;
+}
+
+/* Read WORDS gcov_unsigned_t values from gcov file.  */
+
+static gcov_unsigned_t *
+gcov_read_words (void *buffer, unsigned words)
+{
+  return (gcov_unsigned_t *)gcov_read_bytes (buffer, GCOV_WORD_SIZE * words);
 }
 
 /* Read unsigned value from a coverage file. Sets error flag on file
@@ -510,10 +451,12 @@ GCOV_LINKAGE gcov_unsigned_t
 gcov_read_unsigned (void)
 {
   gcov_unsigned_t value;
-  const gcov_unsigned_t *buffer = gcov_read_words (1);
+  gcov_unsigned_t allocated_buffer[1];
+  gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 1);
 
   if (!buffer)
     return 0;
+
   value = from_file (buffer[0]);
   return value;
 }
@@ -525,7 +468,8 @@ GCOV_LINKAGE gcov_type
 gcov_read_counter (void)
 {
   gcov_type value;
-  const gcov_unsigned_t *buffer = gcov_read_words (2);
+  gcov_unsigned_t allocated_buffer[2];
+  gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2);
 
   if (!buffer)
     return 0;
@@ -533,7 +477,7 @@ gcov_read_counter (void)
   if (sizeof (value) > sizeof (gcov_unsigned_t))
     value |= ((gcov_type) from_file (buffer[1])) << 32;
   else if (buffer[1])
-    gcov_var.error = -1;
+    gcov_var.error = GCOV_FILE_COUNTER_OVERFLOW;
 
   return value;
 }
@@ -602,7 +546,8 @@ gcov_read_string (void)
   if (!length)
     return 0;
 
-  return (const char *) gcov_read_words (length);
+  void *buffer = XNEWVEC (char *, length);
+  return (const char *) gcov_read_bytes (buffer, length);
 }
 #endif
 
@@ -624,27 +569,18 @@ gcov_sync (gcov_position_t base, gcov_unsigned_t length)
 {
   gcov_nonruntime_assert (gcov_var.mode > 0);
   base += length;
-  if (base - gcov_var.start <= gcov_var.length)
-    gcov_var.offset = base - gcov_var.start;
-  else
+#ifdef IN_GCOV_TOOL
+  if (gcov_var.mode == GCOV_MODE_STDIN)
     {
-      gcov_var.offset = gcov_var.length = 0;
-      fseek (gcov_var.file, base << 2, SEEK_SET);
-      gcov_var.start = ftell (gcov_var.file) >> 2;
+      while (gcov_var.pos < base)
+	{
+	  ++gcov_var.pos;
+	  (void)fgetc (gcov_var.file);
+	}
+      return;
     }
-}
 #endif
-
-#if IN_LIBGCOV
-/* Move to a given position in a gcov file.  */
-
-GCOV_LINKAGE void
-gcov_seek (gcov_position_t base)
-{
-  if (gcov_var.offset)
-    gcov_write_block (gcov_var.offset);
-  fseek (gcov_var.file, base << 2, SEEK_SET);
-  gcov_var.start = ftell (gcov_var.file) >> 2;
+  fseek (gcov_var.file, base, SEEK_SET);
 }
 #endif
 
diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h
index 4391d6d50a3..e58c8c763c0 100644
--- a/gcc/gcov-io.h
+++ b/gcc/gcov-io.h
@@ -36,16 +36,14 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
    Numbers are recorded in the 32 bit unsigned binary form of the
    endianness of the machine generating the file. 64 bit numbers are
-   stored as two 32 bit numbers, the low part first.  Strings are
-   padded with 1 to 4 NUL bytes, to bring the length up to a multiple
-   of 4. The number of 4 bytes is stored, followed by the padded
+   stored as two 32 bit numbers, the low part first.
+   The number of bytes is stored, followed by the
    string. Zero length and NULL strings are simply stored as a length
-   of zero (they have no trailing NUL or padding).
+   of zero (they have no trailing NUL).
 
    	int32:  byte3 byte2 byte1 byte0 | byte0 byte1 byte2 byte3
 	int64:  int32:low int32:high
-	string: int32:0 | int32:length char* char:0 padding
-	padding: | char:0 | char:0 char:0 | char:0 char:0 char:0
+	string: int32:0 | int32:length char* char:0
 	item: int32 | int64 | string
 
    The basic format of the notes file is
@@ -56,14 +54,21 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
    	file : int32:magic int32:version int32:stamp record*
 
-   The magic ident is different for the notes and the data files.  The
-   magic ident is used to determine the endianness of the file, when
-   reading.  The version is the same for both files and is derived
-   from gcc's version number. The stamp value is used to synchronize
-   note and data files and to synchronize merging within a data
-   file. It need not be an absolute time stamp, merely a ticker that
-   increments fast enough and cycles slow enough to distinguish
-   different compile/run/compile cycles.
+   A filename header may be used to provide a filename for the data in
+   a stream of data to support gcov in freestanding environments.  This
+   header is used by the merge-stream subcommand of the gcov-tool.  The
+   format of the filename header is
+
+	filename-header : int32:magic int32:version string
+
+   The magic ident is different for the notes and the data files as
+   well as the filename header.  The magic ident is used to determine
+   the endianness of the file, when reading.  The version is the same
+   for both files and is derived from gcc's version number. The stamp
+   value is used to synchronize note and data files and to synchronize
+   merging within a data file. It need not be an absolute time stamp,
+   merely a ticker that increments fast enough and cycles slow enough
+   to distinguish different compile/run/compile cycles.
 
    Although the ident and version are formally 32 bit numbers, they
    are derived from 4 character ASCII strings.  The version number
@@ -164,6 +169,17 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #ifndef GCC_GCOV_IO_H
 #define GCC_GCOV_IO_H
 
+/* GCOV key-value pair linked list type.  */
+
+struct gcov_kvp;
+
+struct gcov_kvp
+{
+  gcov_type value;
+  gcov_type count;
+  struct gcov_kvp *next;
+};
+
 #ifndef IN_LIBGCOV
 /* About the host */
 
@@ -185,6 +201,12 @@ typedef uint64_t gcov_type_unsigned;
 #define GCOV_LOCKED 0
 #endif
 
+#if defined (HOST_HAS_LK_LOCK)
+#define GCOV_LOCKED_WITH_LOCKING 1
+#else
+#define GCOV_LOCKED_WITH_LOCKING 0
+#endif
+
 #define ATTRIBUTE_HIDDEN
 
 #endif /* !IN_LIBGCOV */
@@ -207,6 +229,7 @@ typedef uint64_t gcov_type_unsigned;
 /* File magic. Must not be palindromes.  */
 #define GCOV_DATA_MAGIC ((gcov_unsigned_t)0x67636461) /* "gcda" */
 #define GCOV_NOTE_MAGIC ((gcov_unsigned_t)0x67636e6f) /* "gcno" */
+#define GCOV_FILENAME_MAGIC ((gcov_unsigned_t)0x6763666e) /* "gcfn" */
 
 /* gcov-iov.h is automatically generated by the makefile from
    version.c, it looks like
@@ -224,22 +247,25 @@ typedef uint64_t gcov_type_unsigned;
 /* The record tags.  Values [1..3f] are for tags which may be in either
    file.  Values [41..9f] for those in the note file and [a1..ff] for
    the data file.  The tag value zero is used as an explicit end of
-   file marker -- it is not required to be present.  */
+   file marker -- it is not required to be present.
+   All length values are in bytes.  */
+
+#define GCOV_WORD_SIZE		4
 
 #define GCOV_TAG_FUNCTION	 ((gcov_unsigned_t)0x01000000)
-#define GCOV_TAG_FUNCTION_LENGTH (3)
+#define GCOV_TAG_FUNCTION_LENGTH (3 * GCOV_WORD_SIZE)
 #define GCOV_TAG_BLOCKS		 ((gcov_unsigned_t)0x01410000)
 #define GCOV_TAG_BLOCKS_LENGTH(NUM) (NUM)
 #define GCOV_TAG_ARCS		 ((gcov_unsigned_t)0x01430000)
-#define GCOV_TAG_ARCS_LENGTH(NUM)  (1 + (NUM) * 2)
-#define GCOV_TAG_ARCS_NUM(LENGTH)  (((LENGTH) - 1) / 2)
+#define GCOV_TAG_ARCS_LENGTH(NUM)  (1 + (NUM) * 2 * GCOV_WORD_SIZE)
+#define GCOV_TAG_ARCS_NUM(LENGTH)  (((LENGTH / GCOV_WORD_SIZE) - 1) / 2)
 #define GCOV_TAG_LINES		 ((gcov_unsigned_t)0x01450000)
 #define GCOV_TAG_COUNTER_BASE 	 ((gcov_unsigned_t)0x01a10000)
-#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2)
-#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
+#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE)
+#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH / GCOV_WORD_SIZE) / 2)
 #define GCOV_TAG_OBJECT_SUMMARY  ((gcov_unsigned_t)0xa1000000)
 #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000) /* Obsolete */
-#define GCOV_TAG_SUMMARY_LENGTH (2)
+#define GCOV_TAG_SUMMARY_LENGTH (2 * GCOV_WORD_SIZE)
 #define GCOV_TAG_AFDO_FILE_NAMES ((gcov_unsigned_t)0xaa000000)
 #define GCOV_TAG_AFDO_FUNCTION ((gcov_unsigned_t)0xac000000)
 #define GCOV_TAG_AFDO_WORKING_SET ((gcov_unsigned_t)0xaf000000)
@@ -266,11 +292,14 @@ GCOV_COUNTERS
 #define GCOV_N_VALUE_COUNTERS \
   (GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1)
 
-/* Number of top N value histogram.  */
-#define GCOV_TOPN_VALUES 4
+/* Number of top N counters when being in memory.  */
+#define GCOV_TOPN_MEM_COUNTERS 3
 
-/* Total number of single value counters.  */
-#define GCOV_TOPN_VALUES_COUNTERS (2 * GCOV_TOPN_VALUES + 1)
+/* Number of top N counters in disk representation.  */
+#define GCOV_TOPN_DISK_COUNTERS 2
+
+/* Maximum number of tracked TOP N value profiles.  */
+#define GCOV_TOPN_MAXIMUM_TRACKED_VALUES 32
 
 /* Convert a counter index to a tag.  */
 #define GCOV_TAG_FOR_COUNTER(COUNT)				\
@@ -317,22 +346,19 @@ struct gcov_summary
 /* Functions for reading and writing gcov files. In libgcov you can
    open the file for reading then writing. Elsewhere you can open the
    file either for reading or for writing. When reading a file you may
-   use the gcov_read_* functions, gcov_sync, gcov_position, &
-   gcov_error. When writing a file you may use the gcov_write
-   functions, gcov_seek & gcov_error. When a file is to be rewritten
+   use the gcov_read_* functions, gcov_sync, gcov_position, and
+   gcov_error. When writing a file you may use the gcov_write*
+   functions and gcov_error. When a file is to be rewritten
    you use the functions for reading, then gcov_rewrite then the
    functions for writing.  Your file may become corrupted if you break
    these invariants.  */
 
-#if !IN_LIBGCOV
-GCOV_LINKAGE int gcov_open (const char */*name*/, int /*direction*/);
-#endif
-
 #if !IN_LIBGCOV || defined (IN_GCOV_TOOL)
 GCOV_LINKAGE int gcov_magic (gcov_unsigned_t, gcov_unsigned_t);
 #endif
 
 /* Available everywhere.  */
+GCOV_LINKAGE int gcov_open (const char *, int) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
@@ -344,6 +370,7 @@ char *mangle_path (char const *base);
 
 #if !IN_GCOV
 /* Available outside gcov */
+GCOV_LINKAGE void gcov_write (const void *, unsigned) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN;
 #endif
 
diff --git a/gcc/gcov-tool.c b/gcc/gcov-tool.c
index f8f1cb5d29b..bf473f40ad9 100644
--- a/gcc/gcov-tool.c
+++ b/gcc/gcov-tool.c
@@ -40,12 +40,14 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #endif
 #include <getopt.h>
 
-extern int gcov_profile_merge (struct gcov_info*, struct gcov_info*, int, int);
+extern struct gcov_info *gcov_profile_merge (struct gcov_info*,
+					     struct gcov_info*, int, int);
+extern struct gcov_info *gcov_profile_merge_stream (const char *, int, int);
 extern int gcov_profile_overlap (struct gcov_info*, struct gcov_info*);
 extern int gcov_profile_normalize (struct gcov_info*, gcov_type);
 extern int gcov_profile_scale (struct gcov_info*, float, int, int);
 extern struct gcov_info* gcov_read_profile_dir (const char*, int);
-extern void gcov_do_dump (struct gcov_info *, int);
+extern void gcov_do_dump (struct gcov_info *, int, int);
 extern const char *gcov_get_filename (struct gcov_info *list);
 extern void gcov_set_verbose (void);
 
@@ -123,7 +125,7 @@ gcov_output_files (const char *out, struct gcov_info *profile)
     fatal_error (input_location, "output file %s already exists in folder %s",
 		 filename, out);
 
-  gcov_do_dump (profile, 0);
+  gcov_do_dump (profile, 0, 0);
 
   ret = chdir (pwd);
   if (ret)
@@ -141,26 +143,18 @@ profile_merge (const char *d1, const char *d2, const char *out, int w1, int w2)
 {
   struct gcov_info *d1_profile;
   struct gcov_info *d2_profile;
-  int ret;
+  struct gcov_info *merged_profile;
 
   d1_profile = gcov_read_profile_dir (d1, 0);
-  if (!d1_profile)
-    return 1;
+  d2_profile = gcov_read_profile_dir (d2, 0);
 
-  if (d2)
-    {
-      d2_profile = gcov_read_profile_dir (d2, 0);
-      if (!d2_profile)
-        return 1;
-
-      /* The actual merge: we overwrite to d1_profile.  */
-      ret = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
-
-      if (ret)
-        return ret;
-    }
+  /* The actual merge: we overwrite to d1_profile.  */
+  merged_profile = gcov_profile_merge (d1_profile, d2_profile, w1, w2);
 
-  gcov_output_files (out, d1_profile);
+  if (merged_profile)
+    gcov_output_files (out, merged_profile);
+  else if (verbose)
+    fnotice (stdout, "no profile files were merged\n");
 
   return 0;
 }
@@ -236,6 +230,78 @@ do_merge (int argc, char **argv)
   return profile_merge (argv[optind], argv[optind+1], output_dir, w1, w2);
 }
 
+/* Usage message for profile merge-stream.  */
+
+static void
+print_merge_stream_usage_message (int error_p)
+{
+  FILE *file = error_p ? stderr : stdout;
+
+  fnotice (file, "  merge-stream [options] [<file>]       Merge coverage stream file (or stdin)\n"
+		 "                                        and coverage file contents\n");
+  fnotice (file, "    -v, --verbose                       Verbose mode\n");
+  fnotice (file, "    -w, --weight <w1,w2>                Set weights (float point values)\n");
+}
+
+static const struct option merge_stream_options[] =
+{
+  { "verbose",                no_argument,       NULL, 'v' },
+  { "weight",                 required_argument, NULL, 'w' },
+  { 0, 0, 0, 0 }
+};
+
+/* Print merge-stream usage and exit.  */
+
+static void ATTRIBUTE_NORETURN
+merge_stream_usage (void)
+{
+  fnotice (stderr, "Merge-stream subcomand usage:");
+  print_merge_stream_usage_message (true);
+  exit (FATAL_EXIT_CODE);
+}
+
+/* Driver for profile merge-stream sub-command.  */
+
+static int
+do_merge_stream (int argc, char **argv)
+{
+  int opt;
+  int w1 = 1, w2 = 1;
+  struct gcov_info *merged_profile;
+
+  optind = 0;
+  while ((opt = getopt_long (argc, argv, "vw:",
+			     merge_stream_options, NULL)) != -1)
+    {
+      switch (opt)
+	{
+	case 'v':
+	  verbose = true;
+	  gcov_set_verbose ();
+	  break;
+	case 'w':
+	  sscanf (optarg, "%d,%d", &w1, &w2);
+	  if (w1 < 0 || w2 < 0)
+	    fatal_error (input_location, "weights need to be non-negative");
+	  break;
+	default:
+	  merge_stream_usage ();
+	}
+    }
+
+  if (argc - optind > 1)
+    merge_stream_usage ();
+
+  merged_profile = gcov_profile_merge_stream (argv[optind], w1, w2);
+
+  if (merged_profile)
+    gcov_do_dump (merged_profile, 0, -1);
+  else if (verbose)
+    fnotice (stdout, "no profile files were merged\n");
+
+  return 0;
+}
+
 /* If N_VAL is no-zero, normalize the profile by setting the largest counter
    counter value to N_VAL and scale others counters proportionally.
    Otherwise, multiply the all counters by SCALE.  */
@@ -512,6 +578,7 @@ print_usage (int error_p)
   fnotice (file, "  -h, --help                            Print this help, then exit\n");
   fnotice (file, "  -v, --version                         Print version number, then exit\n");
   print_merge_usage_message (error_p);
+  print_merge_stream_usage_message (error_p);
   print_rewrite_usage_message (error_p);
   print_overlap_usage_message (error_p);
   fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
@@ -602,6 +669,8 @@ main (int argc, char **argv)
 
   if (!strcmp (sub_command, "merge"))
     return do_merge (argc - optind, argv + optind);
+  else if (!strcmp (sub_command, "merge-stream"))
+    return do_merge_stream (argc - optind, argv + optind);
   else if (!strcmp (sub_command, "rewrite"))
     return do_rewrite (argc - optind, argv + optind);
   else if (!strcmp (sub_command, "overlap"))
diff --git a/gcc/gcov.c b/gcc/gcov.c
index a291bac3e9e..d5b6f87f754 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -630,8 +630,8 @@ static void accumulate_line_counts (source_info *);
 static void output_gcov_file (const char *, source_info *);
 static int output_branch_count (FILE *, int, const arc_info *);
 static void output_lines (FILE *, const source_info *);
-static char *make_gcov_file_name (const char *, const char *);
-static char *mangle_name (const char *, char *);
+static string make_gcov_file_name (const char *, const char *);
+static char *mangle_name (const char *);
 static void release_structures (void);
 extern int main (int, char **);
 
@@ -811,7 +811,6 @@ get_cycles_count (line_info &linfo)
      Therefore, operating on a permuted order (i.e., non-sorted) only
      has the effect of permuting the output cycles.  */
 
-  bool loop_found = false;
   gcov_type count = 0;
   for (vector<block_info *>::iterator it = linfo.blocks.begin ();
        it != linfo.blocks.end (); it++)
@@ -819,8 +818,7 @@ get_cycles_count (line_info &linfo)
       arc_vector_t path;
       block_vector_t blocked;
       vector<block_vector_t > block_lists;
-      loop_found |= circuit (*it, path, *it, blocked, block_lists, linfo,
-			     count);
+      circuit (*it, path, *it, blocked, block_lists, linfo, count);
     }
 
   return count;
@@ -1090,6 +1088,41 @@ output_intermediate_json_line (json::array *object,
   object->append (lineo);
 }
 
+/* Strip filename extension in STR.  */
+
+static string
+strip_extention (string str)
+{
+  string::size_type pos = str.rfind ('.');
+  if (pos != string::npos)
+    str = str.substr (0, pos);
+
+  return str;
+}
+
+/* Calcualte md5sum for INPUT string and return it in hex string format.  */
+
+static string
+get_md5sum (const char *input)
+{
+  md5_ctx ctx;
+  char md5sum[16];
+  string str;
+
+  md5_init_ctx (&ctx);
+  md5_process_bytes (input, strlen (input), &ctx);
+  md5_finish_ctx (&ctx, md5sum);
+
+  for (unsigned i = 0; i < 16; i++)
+    {
+      char b[3];
+      sprintf (b, "%02x", (unsigned char)md5sum[i]);
+      str += b;
+    }
+
+  return str;
+}
+
 /* Get the name of the gcov file.  The return value must be free'd.
 
    It appends the '.gcov' extension to the *basename* of the file.
@@ -1099,20 +1132,26 @@ output_intermediate_json_line (json::array *object,
    input: foo.da,       output: foo.da.gcov
    input: a/b/foo.cc,   output: foo.cc.gcov  */
 
-static char *
-get_gcov_intermediate_filename (const char *file_name)
+static string
+get_gcov_intermediate_filename (const char *input_file_name)
 {
-  const char *gcov = ".gcov.json.gz";
-  char *result;
-  const char *cptr;
+  string base = basename (input_file_name);
+  string str = strip_extention (base);
 
-  /* Find the 'basename'.  */
-  cptr = lbasename (file_name);
-
-  result = XNEWVEC (char, strlen (cptr) + strlen (gcov) + 1);
-  sprintf (result, "%s%s", cptr, gcov);
+  if (flag_hash_filenames)
+    {
+      str += "##";
+      str += get_md5sum (input_file_name);
+    }
+  else if (flag_preserve_paths && base != input_file_name)
+    {
+      str += "##";
+      str += mangle_path (input_file_name);
+      str = strip_extention (str);
+    }
 
-  return result;
+  str += ".gcov.json.gz";
+  return str.c_str ();
 }
 
 /* Output the result in JSON intermediate format.
@@ -1363,7 +1402,9 @@ process_all_functions (void)
 static void
 output_gcov_file (const char *file_name, source_info *src)
 {
-  char *gcov_file_name = make_gcov_file_name (file_name, src->coverage.name);
+  string gcov_file_name_str
+    = make_gcov_file_name (file_name, src->coverage.name);
+  const char *gcov_file_name = gcov_file_name_str.c_str ();
 
   if (src->coverage.lines)
     {
@@ -1385,13 +1426,12 @@ output_gcov_file (const char *file_name, source_info *src)
       unlink (gcov_file_name);
       fnotice (stdout, "Removing '%s'\n", gcov_file_name);
     }
-  free (gcov_file_name);
 }
 
 static void
 generate_results (const char *file_name)
 {
-  char *gcov_intermediate_filename;
+  string gcov_intermediate_filename;
 
   for (vector<function_info *>::iterator it = functions.begin ();
        it != functions.end (); it++)
@@ -1492,11 +1532,13 @@ generate_results (const char *file_name)
 	  root->print (&pp);
 	  pp_formatted_text (&pp);
 
-	  gzFile output = gzopen (gcov_intermediate_filename, "w");
+	  fnotice (stdout, "Creating '%s'\n",
+		   gcov_intermediate_filename.c_str ());
+	  gzFile output = gzopen (gcov_intermediate_filename.c_str (), "w");
 	  if (output == NULL)
 	    {
 	      fnotice (stderr, "Cannot open JSON output file %s\n",
-		       gcov_intermediate_filename);
+		       gcov_intermediate_filename.c_str ());
 	      return;
 	    }
 
@@ -1504,7 +1546,7 @@ generate_results (const char *file_name)
 	      || gzclose (output))
 	    {
 	      fnotice (stderr, "Error writing JSON output file %s\n",
-		       gcov_intermediate_filename);
+		       gcov_intermediate_filename.c_str ());
 	      return;
 	    }
 	}
@@ -1718,6 +1760,8 @@ read_graph_file (void)
 	       bbg_file_name, v, e);
     }
   bbg_stamp = gcov_read_unsigned ();
+  /* Read checksum.  */
+  gcov_read_unsigned ();
   bbg_cwd = xstrdup (gcov_read_string ());
   bbg_supports_has_unexecuted_blocks = gcov_read_unsigned ();
 
@@ -1933,6 +1977,9 @@ read_count_file (void)
       goto cleanup;
     }
 
+  /* Read checksum.  */
+  gcov_read_unsigned ();
+
   while ((tag = gcov_read_unsigned ()))
     {
       unsigned length = gcov_read_unsigned ();
@@ -1968,11 +2015,16 @@ read_count_file (void)
 	}
       else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
 	{
+	  int read_length = (int)length;
+	  length = abs (read_length);
 	  if (length != GCOV_TAG_COUNTER_LENGTH (fn->counts.size ()))
 	    goto mismatch;
 
-	  for (ix = 0; ix != fn->counts.size (); ix++)
-	    fn->counts[ix] += gcov_read_counter ();
+	  if (read_length > 0)
+	    for (ix = 0; ix != fn->counts.size (); ix++)
+	      fn->counts[ix] += gcov_read_counter ();
+	  else
+	    length = 0;
 	}
       gcov_sync (base, length);
       if ((error = gcov_is_error ()))
@@ -2487,15 +2539,6 @@ canonicalize_name (const char *name)
   return result;
 }
 
-/* Print hex representation of 16 bytes from SUM and write it to BUFFER.  */
-
-static void
-md5sum_to_hex (const char *sum, char *buffer)
-{
-  for (unsigned i = 0; i < 16; i++)
-    sprintf (buffer + (2 * i), "%02x", (unsigned char)sum[i]);
-}
-
 /* Generate an output file name. INPUT_NAME is the canonicalized main
    input file and SRC_NAME is the canonicalized file name.
    LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation.  With
@@ -2508,77 +2551,42 @@ md5sum_to_hex (const char *sum, char *buffer)
    component.  (Remember, the canonicalized name will already have
    elided '.' components and converted \\ separators.)  */
 
-static char *
+static string
 make_gcov_file_name (const char *input_name, const char *src_name)
 {
-  char *ptr;
-  char *result;
-
-  if (flag_long_names && input_name && strcmp (src_name, input_name))
-    {
-      /* Generate the input filename part.  */
-      result = XNEWVEC (char, strlen (input_name) + strlen (src_name) + 10);
-
-      ptr = result;
-      ptr = mangle_name (input_name, ptr);
-      ptr[0] = ptr[1] = '#';
-      ptr += 2;
-    }
-  else
-    {
-      result = XNEWVEC (char, strlen (src_name) + 10);
-      ptr = result;
-    }
-
-  ptr = mangle_name (src_name, ptr);
-  strcpy (ptr, ".gcov");
+  string str;
 
   /* When hashing filenames, we shorten them by only using the filename
      component and appending a hash of the full (mangled) pathname.  */
   if (flag_hash_filenames)
+    str = (string (mangle_name (src_name)) + "##"
+	   + get_md5sum (src_name) + ".gcov");
+  else
     {
-      md5_ctx ctx;
-      char md5sum[16];
-      char md5sum_hex[33];
-
-      md5_init_ctx (&ctx);
-      md5_process_bytes (src_name, strlen (src_name), &ctx);
-      md5_finish_ctx (&ctx, md5sum);
-      md5sum_to_hex (md5sum, md5sum_hex);
-      free (result);
+      if (flag_long_names && input_name && strcmp (src_name, input_name) != 0)
+	{
+	  str += mangle_name (input_name);
+	  str += "##";
+	}
 
-      result = XNEWVEC (char, strlen (src_name) + 50);
-      ptr = result;
-      ptr = mangle_name (src_name, ptr);
-      ptr[0] = ptr[1] = '#';
-      ptr += 2;
-      memcpy (ptr, md5sum_hex, 32);
-      ptr += 32;
-      strcpy (ptr, ".gcov");
+      str += mangle_name (src_name);
+      str += ".gcov";
     }
 
-  return result;
+  return str;
 }
 
 /* Mangle BASE name, copy it at the beginning of PTR buffer and
    return address of the \0 character of the buffer.  */
 
 static char *
-mangle_name (char const *base, char *ptr)
+mangle_name (char const *base)
 {
-  size_t len;
-
   /* Generate the source filename part.  */
   if (!flag_preserve_paths)
-    base = lbasename (base);
+    return xstrdup (lbasename (base));
   else
-    base = mangle_path (base);
-
-  len = strlen (base);
-  memcpy (ptr, base, len);
-  ptr += len;
-
-  return ptr;
+    return mangle_path (base);
 }
 
 /* Scan through the bb_data for each line in the block, increment
@@ -2831,7 +2839,6 @@ read_line (FILE *file)
   static char *string;
   static size_t string_len;
   size_t pos = 0;
-  char *ptr;
 
   if (!string_len)
     {
@@ -2839,7 +2846,7 @@ read_line (FILE *file)
       string = XNEWVEC (char, string_len);
     }
 
-  while ((ptr = fgets (string + pos, string_len - pos, file)))
+  while (fgets (string + pos, string_len - pos, file))
     {
       size_t len = strlen (string + pos);
 
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 9fbfa90e538..43e9b6bb4db 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -295,7 +295,8 @@ ipa_profile_generate_summary (void)
 		      speculative_call_summary *csum
 			= call_sums->get_create (e);
 
-		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+		      for (unsigned j = 0; j < GCOV_TOPN_MAXIMUM_TRACKED_VALUES;
+			   j++)
 			{
 			  if (!get_nth_most_common_value (NULL, "indirect call",
 							  h, &val, &count, &all,
@@ -342,7 +343,7 @@ ipa_profile_write_edge_summary (lto_simple_output_block *ob,
 
   len = csum->speculative_call_targets.length ();
 
-  gcc_assert (len <= GCOV_TOPN_VALUES);
+  gcc_assert (len <= GCOV_TOPN_MAXIMUM_TRACKED_VALUES);
 
   streamer_write_hwi_stream (ob->main_stream, len);
 
@@ -448,8 +449,7 @@ ipa_profile_read_edge_summary (class lto_input_block *ib, cgraph_edge *edge)
   unsigned i, len;
 
   len = streamer_read_hwi (ib);
-  gcc_assert (len <= GCOV_TOPN_VALUES);
-
+  gcc_assert (len <= GCOV_TOPN_MAXIMUM_TRACKED_VALUES);
   speculative_call_summary *csum = call_sums->get_create (edge);
 
   for (i = 0; i < len; i++)
@@ -885,8 +885,7 @@ ipa_profile (void)
 				   item.target_probability
 				     / (float) REG_BR_PROB_BASE);
 			}
-		      if (item.target_probability
-		 	  < REG_BR_PROB_BASE / GCOV_TOPN_VALUES / 2)
+		      if (item.target_probability < REG_BR_PROB_BASE / 2)
 			{
 			  nuseless++;
 			  if (dump_file)
diff --git a/gcc/lto-compress.c b/gcc/lto-compress.c
index d19338b396a..af41bb49381 100644
--- a/gcc/lto-compress.c
+++ b/gcc/lto-compress.c
@@ -250,7 +250,6 @@ lto_compression_zlib (struct lto_compression_stream *stream)
   const size_t outbuf_length = Z_BUFFER_LENGTH;
   unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length);
   z_stream out_stream;
-  size_t compressed_bytes = 0;
   int status;
 
   gcc_assert (stream->is_compression);
@@ -282,7 +281,6 @@ lto_compression_zlib (struct lto_compression_stream *stream)
 
       stream->callback ((const char *) outbuf, out_bytes, stream->opaque);
       lto_stats.num_compressed_il_bytes += out_bytes;
-      compressed_bytes += out_bytes;
 
       cursor += in_bytes;
       remaining -= in_bytes;
@@ -342,7 +340,6 @@ lto_uncompression_zlib (struct lto_compression_stream *stream)
   size_t remaining = stream->bytes;
   const size_t outbuf_length = Z_BUFFER_LENGTH;
   unsigned char *outbuf = (unsigned char *) xmalloc (outbuf_length);
-  size_t uncompressed_bytes = 0;
 
   gcc_assert (!stream->is_compression);
   timevar_push (TV_IPA_LTO_DECOMPRESS);
@@ -378,7 +375,6 @@ lto_uncompression_zlib (struct lto_compression_stream *stream)
 
 	  stream->callback ((const char *) outbuf, out_bytes, stream->opaque);
 	  lto_stats.num_uncompressed_il_bytes += out_bytes;
-	  uncompressed_bytes += out_bytes;
 
 	  cursor += in_bytes;
 	  remaining -= in_bytes;
diff --git a/gcc/opts.c b/gcc/opts.c
index 73162528938..2a735d243dc 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -2606,6 +2606,10 @@ common_handle_option (struct gcc_options *opts,
       SET_OPTION_IF_UNSET (opts, opts_set, flag_ipa_bit_cp, value);
       break;
 
+    case OPT_fprofile_info_section:
+      opts->x_profile_info_section = ".gcov_info";
+      break;
+
     case OPT_fpatchable_function_entry_:
       {
 	char *patch_area_arg = xstrdup (arg);
diff --git a/gcc/profile.c b/gcc/profile.c
index 2e5ab0d3415..4dc81341141 100644
--- a/gcc/profile.c
+++ b/gcc/profile.c
@@ -765,26 +765,22 @@ compute_branch_probabilities (unsigned cfg_checksum, unsigned lineno_checksum)
 static void
 sort_hist_values (histogram_value hist)
 {
-  /* counters[2] equal to -1 means that all counters are invalidated.  */
-  if (hist->hvalue.counters[2] == -1)
-    return;
-
   gcc_assert (hist->type == HIST_TYPE_TOPN_VALUES
 	      || hist->type == HIST_TYPE_INDIR_CALL);
 
-  gcc_assert (hist->n_counters == GCOV_TOPN_VALUES_COUNTERS);
-
+  int counters = hist->hvalue.counters[1];
+  for (int i = 0; i < counters - 1; i++)
   /* Hist value is organized as:
-     [total_executions, value1, counter1, ..., value4, counter4]
+     [total_executions, N, counter1, ..., valueN, counterN]
      Use decrease bubble sort to rearrange it.  The sort starts from <value1,
      counter1> and compares counter first.  If counter is same, compares the
      value, exchange it if small to keep stable.  */
-  for (unsigned i = 0; i < GCOV_TOPN_VALUES - 1; i++)
+
     {
       bool swapped = false;
-      for (unsigned j = 0; j < GCOV_TOPN_VALUES - 1 - i; j++)
+      for (int j = 0; j < counters - 1 - i; j++)
 	{
-	  gcov_type *p = &hist->hvalue.counters[2 * j + 1];
+	  gcov_type *p = &hist->hvalue.counters[2 * j + 2];
 	  if (p[1] < p[3] || (p[1] == p[3] && p[0] < p[2]))
 	    {
 	      std::swap (p[0], p[2]);
@@ -847,31 +843,43 @@ compute_value_histograms (histogram_values values, unsigned cfg_checksum,
       gimple *stmt = hist->hvalue.stmt;
 
       t = (int) hist->type;
+      bool topn_p = (hist->type == HIST_TYPE_TOPN_VALUES
+		     || hist->type == HIST_TYPE_INDIR_CALL);
 
-      aact_count = act_count[t];
-
-      if (act_count[t])
-        act_count[t] += hist->n_counters;
-
-      gimple_add_histogram_value (cfun, stmt, hist);
-      hist->hvalue.counters =  XNEWVEC (gcov_type, hist->n_counters);
-      for (j = 0; j < hist->n_counters; j++)
-        if (aact_count)
-          hist->hvalue.counters[j] = aact_count[j];
-        else
-          hist->hvalue.counters[j] = 0;
-
-      if (hist->type == HIST_TYPE_TOPN_VALUES
-	  || hist->type == HIST_TYPE_INDIR_CALL)
+      /* TOP N counter uses variable number of counters.  */
+      if (topn_p)
 	{
-	  /* Each count value is multiplied by GCOV_TOPN_VALUES.  */
-	  if (hist->hvalue.counters[2] != -1)
-	    for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
-	      hist->hvalue.counters[2 * i + 2]
-		= RDIV (hist->hvalue.counters[2 * i + 2], GCOV_TOPN_VALUES);
-
+	  unsigned total_size;
+	  if (act_count[t])
+	    total_size = 2 + 2 * act_count[t][1];
+	  else
+	    total_size = 2;
+	  gimple_add_histogram_value (cfun, stmt, hist);
+	  hist->n_counters = total_size;
+	  hist->hvalue.counters = XNEWVEC (gcov_type, hist->n_counters);
+	  for (j = 0; j < hist->n_counters; j++)
+	    if (act_count[t])
+	      hist->hvalue.counters[j] = act_count[t][j];
+	    else
+	      hist->hvalue.counters[j] = 0;
+	  act_count[t] += hist->n_counters;
 	  sort_hist_values (hist);
 	}
+      else
+	{
+	  aact_count = act_count[t];
+
+	  if (act_count[t])
+	    act_count[t] += hist->n_counters;
+
+	  gimple_add_histogram_value (cfun, stmt, hist);
+	  hist->hvalue.counters = XNEWVEC (gcov_type, hist->n_counters);
+	  for (j = 0; j < hist->n_counters; j++)
+	    if (aact_count)
+	      hist->hvalue.counters[j] = aact_count[j];
+	    else
+	      hist->hvalue.counters[j] = 0;
+	}
 
       /* Time profiler counter is not related to any statement,
          so that we have to read the counter and set the value to
diff --git a/gcc/system.h b/gcc/system.h
index 263070169ab..6ba6ff8de9c 100644
--- a/gcc/system.h
+++ b/gcc/system.h
@@ -353,6 +353,10 @@ extern int errno;
 # endif
 #endif
 
+#ifdef HAVE_SYS_LOCKING_H
+# include <sys/locking.h>
+#endif
+
 #ifndef SEEK_SET
 # define SEEK_SET 0
 # define SEEK_CUR 1
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index 7cb04f30bdb..af23783e23c 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -2022,7 +2022,7 @@ pch_option_mismatch (const char *option)
 /* Default version of pch_valid_p.  */
 
 const char *
-default_pch_valid_p (const void *data_p, size_t len)
+default_pch_valid_p (const void *data_p, size_t len ATTRIBUTE_UNUSED)
 {
   struct cl_option_state state;
   const char *data = (const char *)data_p;
@@ -2043,7 +2043,6 @@ default_pch_valid_p (const void *data_p, size_t len)
 
       memcpy (&tf, data, sizeof (target_flags));
       data += sizeof (target_flags);
-      len -= sizeof (target_flags);
       r = targetm.check_pch_target_flags (tf);
       if (r != NULL)
 	return r;
@@ -2055,7 +2054,6 @@ default_pch_valid_p (const void *data_p, size_t len)
 	if (memcmp (data, state.data, state.size) != 0)
 	  return pch_option_mismatch (cl_options[i].opt_text);
 	data += state.size;
-	len -= state.size;
       }
 
   return NULL;
diff --git a/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c b/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c
new file mode 100644
index 00000000000..91502223d12
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gcov-info-to-gcda.c
@@ -0,0 +1,82 @@
+/* { dg-do run } */
+/* { dg-skip-if "profile-info-section" { powerpc-ibm-aix* } } */
+/* { dg-options "-fprofile-arcs -fprofile-info-section" } */
+
+#define assert(expr)                                            \
+  ((expr)                                                       \
+   ? (void)0                                                    \
+   : (__builtin_printf ("%s:%i: Assertion `%s' failed.\n",      \
+                        __FILE__, __LINE__, #expr),             \
+      __builtin_abort ()))
+
+struct gcov_info;
+
+extern void
+__gcov_info_to_gcda (const struct gcov_info *__info,
+		     void (*__filename_fn) (const char *, void *),
+		     void (*__dump_fn) (const void *, unsigned, void *),
+		     void *(*__allocate_fn) (unsigned, void *),
+		     void *__arg);
+
+extern void
+__gcov_filename_to_gcfn (const char *__filename,
+			 void (*__dump_fn) (const void *, unsigned, void *),
+			 void *__arg);
+
+extern const struct gcov_info *my_info;
+
+static unsigned counter;
+
+static unsigned counter_after_filename;
+
+static int check_zero;
+
+static int check_after_filename;
+
+static void
+dump (const void *d, unsigned n, void *arg)
+{
+  unsigned *m = (unsigned *)arg;
+  assert (arg == &counter);
+
+  if (*m == 0)
+  {
+    const unsigned *u = d;
+    assert (*u == 0x6763666e);
+    check_zero = 1;
+  }
+  else if (*m == counter_after_filename)
+  {
+    const unsigned *u = d;
+    assert (*u == 0x67636461);
+    check_after_filename = 1;
+  }
+
+  *m += n;
+}
+
+static void
+filename (const char *f, void *arg)
+{
+  assert (arg == &counter);
+  assert (__builtin_strstr (f, "gcov-info-to-gcda.c") == 0);
+  __gcov_filename_to_gcfn (f, dump, arg);
+  counter_after_filename = counter;
+}
+
+static void *
+allocate (unsigned length, void *arg)
+{
+  assert (arg == &counter);
+  return __builtin_malloc (length);
+}
+
+int main()
+{
+  __asm__ volatile (".set my_info, .LPBX2");
+  __gcov_info_to_gcda (my_info, filename, dump, allocate, &counter);
+  assert (counter > 8);
+  assert (check_zero);
+  assert (check_after_filename);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/profile-info-section.c b/gcc/testsuite/gcc.dg/profile-info-section.c
new file mode 100644
index 00000000000..c27fe17084e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/profile-info-section.c
@@ -0,0 +1,20 @@
+/* { dg-options "-fprofile-arcs -fprofile-info-section -fdump-tree-optimized" } */
+
+int foo()
+{
+  return 0;
+}
+
+int bar()
+{
+  return 1;
+}
+
+int main ()
+{
+  return foo ();
+}
+
+/* { dg-final { scan-tree-dump-not "__gcov_init" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__gcov_exit" "optimized" } } */
+/* { dg-final { scan-assembler "\.gcov_info" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c
new file mode 100644
index 00000000000..7bda4aedfc8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-malloc.c
@@ -0,0 +1,49 @@
+/* { dg-options "-O2 -ldl -fprofile-correction" } */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <dlfcn.h>
+
+int global;
+int global2;
+
+void report1 (size_t size)
+{
+  global++;
+}
+
+void report2 (size_t size)
+{
+  global2++;
+}
+
+typedef void (*tp) (size_t);
+static tp fns[] = {report1, report2};
+
+void* malloc(size_t size)
+{
+  static void* (*real_malloc)(size_t) = NULL;
+  if (!real_malloc)
+      real_malloc = dlsym(RTLD_NEXT, "malloc");
+
+  void *p = real_malloc (size);
+  fns[size % 2] (size);
+  // fprintf(stderr, "malloc(%d) = %p\n", size, p);
+  return p;
+}
+
+void *calloc (size_t n, size_t size)
+{
+  void *ptr = malloc (n * size);
+  __builtin_memset (ptr, 0, n * size);
+  return ptr;
+}
+
+void *ptr;
+
+int main()
+{
+  ptr = malloc (16);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-prof/pr97461.c b/gcc/testsuite/gcc.dg/tree-prof/pr97461.c
new file mode 100644
index 00000000000..f97fb9c9c80
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/pr97461.c
@@ -0,0 +1,58 @@
+/* PR gcov-profile/97461 */
+/* { dg-options "-O2 -ldl -fprofile-correction" } */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int malloc_depth = 0;
+
+static char memory[128* 1024];
+static size_t memory_p = 0;
+
+void f1(void) {}
+void f2(void) {}
+
+typedef void (*fun_t)(void);
+static const fun_t funs[2] = { f1, f2, };
+
+static void * malloc_impl(size_t size) {
+    void * r = &memory[memory_p];
+    memory_p += size;
+
+    // force TOPN profile
+    funs[size % 2]();
+    return r;
+}
+
+// Override default malloc, check it it get s called recursively
+void * malloc(size_t size) {
+    // Must not be called recursively. Malloc implementation does not support it.
+    if (malloc_depth != 0) __builtin_trap();
+
+    ++malloc_depth;
+      void * r = malloc_impl(size);
+    --malloc_depth;
+    return r;
+}
+
+// Called from gcov
+void *calloc(size_t nmemb, size_t size) {
+    // Must not be called recursively.  Malloc implementation does not support it.
+    if (malloc_depth != 0) __builtin_trap();
+
+    ++malloc_depth;
+      void * r = malloc_impl(size * nmemb);
+      memset(r, 0, size * nmemb);
+    --malloc_depth;
+    return r;
+}
+
+void free(void *ptr){}
+
+int main() {
+    void * p = malloc(8);
+    return p != 0 ? 0 : 1;
+}
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 6c0838261a1..a403fd9fe10 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -247,7 +247,7 @@ gimple_gen_edge_profiler (int edgeno, edge e)
     {
       /* __atomic_fetch_add (&counter, 1, MEMMODEL_RELAXED); */
       tree addr = tree_coverage_counter_addr (GCOV_COUNTER_ARCS, edgeno);
-      tree f = builtin_decl_explicit (LONG_LONG_TYPE_SIZE > 32
+      tree f = builtin_decl_explicit (TYPE_PRECISION (gcov_type_node) > 32
 				      ? BUILT_IN_ATOMIC_FETCH_ADD_8:
 				      BUILT_IN_ATOMIC_FETCH_ADD_4);
       gcall *stmt = gimple_build_call (f, 3, addr, one,
@@ -522,7 +522,7 @@ gimple_gen_time_profiler (unsigned tag)
 			  tree_time_profiler_counter);
       gassign *assign = gimple_build_assign (ptr, NOP_EXPR, addr);
       gsi_insert_before (&gsi, assign, GSI_NEW_STMT);
-      tree f = builtin_decl_explicit (LONG_LONG_TYPE_SIZE > 32
+      tree f = builtin_decl_explicit (TYPE_PRECISION (gcov_type_node) > 32
 				      ? BUILT_IN_ATOMIC_ADD_FETCH_8:
 				      BUILT_IN_ATOMIC_ADD_FETCH_4);
       gcall *stmt = gimple_build_call (f, 3, ptr, one,
diff --git a/gcc/tsystem.h b/gcc/tsystem.h
index 7be25131364..aaa2cc60551 100644
--- a/gcc/tsystem.h
+++ b/gcc/tsystem.h
@@ -59,7 +59,7 @@ extern int atexit (void (*)(void));
 #endif
 
 #ifndef abort
-extern void abort (void) __attribute__ ((__noreturn__));
+#define abort() __builtin_trap ()
 #endif
 
 #ifndef strlen
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 9d60b56c43a..95d33c63a0c 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -265,16 +265,15 @@ dump_histogram_value (FILE *dump_file, histogram_value hist)
 		    ? "Top N value counter" : "Indirect call counter"));
 	  if (hist->hvalue.counters)
 	    {
-	      fprintf (dump_file, " all: %" PRId64 "%s, values: ",
-		       (int64_t) abs_hwi (hist->hvalue.counters[0]),
-		       hist->hvalue.counters[0] < 0
-		       ? " (values missing)": "");
-	      for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
+	      unsigned count = hist->hvalue.counters[1];
+	      fprintf (dump_file, " all: %" PRId64 ", %" PRId64 " values: ",
+		       (int64_t) hist->hvalue.counters[0], (int64_t) count);
+	      for (unsigned i = 0; i < count; i++)
 		{
 		  fprintf (dump_file, "[%" PRId64 ":%" PRId64 "]",
-			   (int64_t) hist->hvalue.counters[2 * i + 1],
-			   (int64_t) hist->hvalue.counters[2 * i + 2]);
-		  if (i != GCOV_TOPN_VALUES - 1)
+			   (int64_t) hist->hvalue.counters[2 * i + 2],
+			   (int64_t) hist->hvalue.counters[2 * i + 3]);
+		  if (i != count - 1)
 		    fprintf (dump_file, ", ");
 		}
 	      fprintf (dump_file, ".\n");
@@ -380,7 +379,6 @@ stream_in_histogram_value (class lto_input_block *ib, gimple *stmt)
 
 	case HIST_TYPE_TOPN_VALUES:
 	case HIST_TYPE_INDIR_CALL:
-	  ncounters = GCOV_TOPN_VALUES_COUNTERS;
 	  break;
 
 	case HIST_TYPE_IOR:
@@ -391,12 +389,31 @@ stream_in_histogram_value (class lto_input_block *ib, gimple *stmt)
 	default:
 	  gcc_unreachable ();
 	}
-      new_val->hvalue.counters = XNEWVAR (gcov_type,
-					  sizeof (*new_val->hvalue.counters)
-					  * ncounters);
-      new_val->n_counters = ncounters;
-      for (i = 0; i < ncounters; i++)
-	new_val->hvalue.counters[i] = streamer_read_gcov_count (ib);
+
+      /* TOP N counters have variable number of counters.  */
+      if (type == HIST_TYPE_INDIR_CALL || type == HIST_TYPE_TOPN_VALUES)
+	{
+	  gcov_type total = streamer_read_gcov_count (ib);
+	  gcov_type ncounters = streamer_read_gcov_count (ib);
+	  new_val->hvalue.counters = XNEWVAR (gcov_type,
+					      sizeof (*new_val->hvalue.counters)
+					      * (2 + 2 * ncounters));
+	  new_val->hvalue.counters[0] = total;
+	  new_val->hvalue.counters[1] = ncounters;
+	  new_val->n_counters = 2 + 2 * ncounters;
+	  for (i = 0; i < 2 * ncounters; i++)
+	    new_val->hvalue.counters[2 + i] = streamer_read_gcov_count (ib);
+	}
+      else
+	{
+	  new_val->hvalue.counters = XNEWVAR (gcov_type,
+					      sizeof (*new_val->hvalue.counters)
+					      * ncounters);
+	  new_val->n_counters = ncounters;
+	  for (i = 0; i < ncounters; i++)
+	    new_val->hvalue.counters[i] = streamer_read_gcov_count (ib);
+	}
+
       if (!next_p)
 	gimple_add_histogram_value (cfun, stmt, new_val);
       else
@@ -741,15 +758,17 @@ get_nth_most_common_value (gimple *stmt, const char *counter_type,
 			   histogram_value hist, gcov_type *value,
 			   gcov_type *count, gcov_type *all, unsigned n)
 {
-  gcc_assert (n < GCOV_TOPN_VALUES);
+  unsigned counters = hist->hvalue.counters[1];
+  if (n >= counters)
+    return false;
 
   *count = 0;
   *value = 0;
 
   gcov_type read_all = abs_hwi (hist->hvalue.counters[0]);
 
-  gcov_type v = hist->hvalue.counters[2 * n + 1];
-  gcov_type c = hist->hvalue.counters[2 * n + 2];
+  gcov_type v = hist->hvalue.counters[2 * n + 2];
+  gcov_type c = hist->hvalue.counters[2 * n + 3];
 
   if (hist->hvalue.counters[0] < 0
       && (flag_profile_reproducible == PROFILE_REPRODUCIBILITY_PARALLEL_RUNS
@@ -1436,7 +1455,7 @@ dump_ic_profile (gimple_stmt_iterator *gsi)
   count = 0;
   all = histogram->hvalue.counters[0];
 
-  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+  for (unsigned j = 0; j < GCOV_TOPN_MAXIMUM_TRACKED_VALUES; j++)
     {
       if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
 				      &count, &all, j))
@@ -1905,7 +1924,7 @@ gimple_find_values_to_profile (histogram_values *values)
 
 	case HIST_TYPE_TOPN_VALUES:
 	case HIST_TYPE_INDIR_CALL:
-	  hist->n_counters = GCOV_TOPN_VALUES_COUNTERS;
+	  hist->n_counters = GCOV_TOPN_MEM_COUNTERS;
 	  break;
 
         case HIST_TYPE_TIME_PROFILE:
diff --git a/libcpp/charset.c b/libcpp/charset.c
index d9281c5fb97..a7dc0aebaeb 100644
--- a/libcpp/charset.c
+++ b/libcpp/charset.c
@@ -1429,7 +1429,6 @@ convert_oct (cpp_reader *pfile, const uchar *from, const uchar *limit,
   cppchar_t c, n = 0;
   size_t width = cvt.width;
   size_t mask = width_to_mask (width);
-  bool overflow = false;
 
   /* loc_reader and ranges must either be both NULL, or both be non-NULL.  */
   gcc_assert ((loc_reader != NULL) == (ranges != NULL));
@@ -1442,7 +1441,6 @@ convert_oct (cpp_reader *pfile, const uchar *from, const uchar *limit,
       from++;
       if (loc_reader)
 	char_range.m_finish = loc_reader->get_next ().m_finish;
-      overflow |= n ^ (n << 3 >> 3);
       n = (n << 3) + c - '0';
     }
 
diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog
index a49027e43b6..794015a315d 100644
--- a/libgcc/ChangeLog
+++ b/libgcc/ChangeLog
@@ -474,6 +474,8 @@
 	* config/mips/mips16.S: Likewise.
 	* config/mips/vr4120-div.S: Likewise.
 
+=======
+>>>>>>> e9ee848dcdc6... Remove wrong bail out in prune_topn_counter.
 2020-01-23  Martin Liska  <mliska@suse.cz>
 
 	* libgcov-driver.c (prune_topn_counter): Remove
diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in
index 851e7657d07..07aa4fdb69c 100644
--- a/libgcc/Makefile.in
+++ b/libgcc/Makefile.in
@@ -907,7 +907,7 @@ LIBGCOV_PROFILER = _gcov_interval_profiler				\
 LIBGCOV_INTERFACE = _gcov_dump _gcov_flush _gcov_fork			\
 	_gcov_execl _gcov_execlp					\
 	_gcov_execle _gcov_execv _gcov_execvp _gcov_execve _gcov_reset
-LIBGCOV_DRIVER = _gcov
+LIBGCOV_DRIVER = _gcov _gcov_info_to_gcda
 
 libgcov-merge-objects = $(patsubst %,%$(objext),$(LIBGCOV_MERGE))
 libgcov-profiler-objects = $(patsubst %,%$(objext),$(LIBGCOV_PROFILER))
diff --git a/libgcc/config.in b/libgcc/config.in
index 5be5321d258..f93c64a00c3 100644
--- a/libgcc/config.in
+++ b/libgcc/config.in
@@ -49,6 +49,9 @@
 /* Define to 1 if you have the <sys/auxv.h> header file. */
 #undef HAVE_SYS_AUXV_H
 
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#undef HAVE_SYS_MMAN_H
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
diff --git a/libgcc/configure b/libgcc/configure
index 26bf75789e0..fcf3d098fdd 100755
--- a/libgcc/configure
+++ b/libgcc/configure
@@ -4458,7 +4458,7 @@ as_fn_arith $ac_cv_sizeof_long_double \* 8 && long_double_type_size=$as_val
 
 for ac_header in inttypes.h stdint.h stdlib.h ftw.h \
 	unistd.h sys/stat.h sys/types.h \
-	string.h strings.h memory.h sys/auxv.h
+	string.h strings.h memory.h sys/auxv.h sys/mman.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_preproc "$LINENO" "$ac_header" "$as_ac_Header"
@@ -4912,7 +4912,9 @@ case "$host" in
     case "$enable_cet" in
       auto)
 	# Check if target supports multi-byte NOPs
-	# and if assembler supports CET insn.
+	# and if compiler and assembler support CET insn.
+	cet_save_CFLAGS="$CFLAGS"
+	CFLAGS="$CFLAGS -fcf-protection"
 	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
diff --git a/libgcc/configure.ac b/libgcc/configure.ac
index bff6e54f22e..47e884016e8 100644
--- a/libgcc/configure.ac
+++ b/libgcc/configure.ac
@@ -224,7 +224,7 @@ AC_SUBST(long_double_type_size)
 
 AC_CHECK_HEADERS(inttypes.h stdint.h stdlib.h ftw.h \
 	unistd.h sys/stat.h sys/types.h \
-	string.h strings.h memory.h sys/auxv.h)
+	string.h strings.h memory.h sys/auxv.h sys/mman.h)
 AC_HEADER_STDC
 
 # Check for decimal float support.
diff --git a/libgcc/gcov.h b/libgcc/gcov.h
index f1581914dde..aa2f675ae98 100644
--- a/libgcc/gcov.h
+++ b/libgcc/gcov.h
@@ -25,6 +25,8 @@
 #ifndef GCC_GCOV_H
 #define GCC_GCOV_H
 
+struct gcov_info;
+
 /* Set all counters to zero.  */
 
 extern void __gcov_reset (void);
@@ -38,4 +40,36 @@ extern void __gcov_dump (void);
 
 extern void __gcov_flush (void);
 
+/* Convert the gcov information referenced by INFO to a gcda data stream.
+   The FILENAME_FN callback is called exactly once with the filename associated
+   with the gcov information.  The filename may be NULL.  Afterwards, the
+   DUMP_FN callback is subsequently called with chunks (the begin and length of
+   the chunk are passed as the first two callback parameters) of the gcda data
+   stream.  The ALLOCATE_FN callback shall allocate memory with a size in
+   characters specified by the first callback parameter.  The ARG parameter is
+   a user-provided argument passed as the last argument to the callback
+   functions.  It is recommended to use the __gcov_filename_to_gcfn()
+   in the filename callback function.  */
+
+extern void
+__gcov_info_to_gcda (const struct gcov_info *__info,
+		     void (*__filename_fn) (const char *, void *),
+		     void (*__dump_fn) (const void *, unsigned, void *),
+		     void *(*__allocate_fn) (unsigned, void *),
+		     void *__arg);
+
+/* Convert the FILENAME to a gcfn data stream.  The DUMP_FN callback is
+   subsequently called with chunks (the begin and length of the chunk are
+   passed as the first two callback parameters) of the gcfn data stream.
+   The ARG parameter is a user-provided argument passed as the last
+   argument to the DUMP_FN callback function.  This function is intended
+   to be used by the filename callback of __gcov_info_to_gcda().  The gcfn
+   data stream is used by the merge-stream subcommand of the gcov-tool to
+   get the filename associated with a gcda data stream.  */
+
+extern void
+__gcov_filename_to_gcfn (const char *__filename,
+			 void (*__dump_fn) (const void *, unsigned, void *),
+			 void *__arg);
+
 #endif /* GCC_GCOV_H */
diff --git a/libgcc/libgcov-driver-system.c b/libgcc/libgcov-driver-system.c
index 031f057e318..0377d77a1e7 100644
--- a/libgcc/libgcov-driver-system.c
+++ b/libgcc/libgcov-driver-system.c
@@ -261,7 +261,8 @@ allocate_filename_struct (struct gcov_filename *gf)
 
 static int
 gcov_exit_open_gcda_file (struct gcov_info *gi_ptr,
-			  struct gcov_filename *gf)
+			  struct gcov_filename *gf,
+			  int mode)
 {
   int append_slash = 0;
   const char *fname = gi_ptr->filename;
@@ -309,7 +310,7 @@ gcov_exit_open_gcda_file (struct gcov_info *gi_ptr,
 
   gf->filename = replace_filename_variables (gf->filename);
 
-  if (!gcov_open (gf->filename))
+  if (!gcov_open (gf->filename, mode))
     {
       /* Open failed likely due to missed directory.
          Create directory and retry to open file. */
@@ -318,7 +319,7 @@ gcov_exit_open_gcda_file (struct gcov_info *gi_ptr,
           fprintf (stderr, "profiling:%s:Skip\n", gf->filename);
           return -1;
         }
-      if (!gcov_open (gf->filename))
+      if (!gcov_open (gf->filename, mode))
         {
           fprintf (stderr, "profiling:%s:Cannot open\n", gf->filename);
           return -1;
diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c
index 37438883d37..da1f7b9affb 100644
--- a/libgcc/libgcov-driver.c
+++ b/libgcc/libgcov-driver.c
@@ -26,6 +26,18 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include "libgcov.h"
 #include "gcov-io.h"
 
+/* Return 1, if all counter values are zero, otherwise 0. */
+
+static inline int
+are_all_counters_zero (const struct gcov_ctr_info *ci_ptr)
+{
+  for (unsigned i = 0; i < ci_ptr->num; i++)
+    if (ci_ptr->values[i] != 0)
+      return 0;
+
+  return 1;
+}
+
 #if defined(inhibit_libc)
 /* If libc and its header files are not available, provide dummy functions.  */
 
@@ -36,14 +48,32 @@ void __gcov_init (struct gcov_info *p __attribute__ ((unused))) {}
 #else /* inhibit_libc */
 
 #include <string.h>
+
 #if GCOV_LOCKED
 #include <fcntl.h>
 #include <errno.h>
 #include <sys/stat.h>
+#elif GCOV_LOCKED_WITH_LOCKING
+#include <fcntl.h>
+#include <sys/locking.h>
+#include <sys/stat.h>
 #endif
 
-#ifdef L_gcov
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
 
+#endif /* inhibit_libc */
+
+#if defined(L_gcov) && !defined(inhibit_libc)
+#define NEED_L_GCOV
+#endif
+
+#if defined(L_gcov_info_to_gcda) && !IN_GCOV_TOOL
+#define NEED_L_GCOV_INFO_TO_GCDA
+#endif
+
+#ifdef NEED_L_GCOV
 /* A utility function for outputting errors.  */
 static int gcov_error (const char *, ...);
 
@@ -213,51 +243,6 @@ static struct gcov_fn_buffer *fn_buffer;
 /* Including system dependent components. */
 #include "libgcov-driver-system.c"
 
-/* Prune TOP N value COUNTERS.  It's needed in order to preserve
-   reproducibility of builds.  */
-
-static void
-prune_topn_counter (gcov_type *counters, gcov_type all)
-{
-  for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
-    if (counters[2 * i + 1] < all)
-      {
-	counters[2 * i] = 0;
-	counters[2 * i + 1] = 0;
-      }
-}
-
-/* Prune counters so that they are ready to store or merge.  */
-
-static void
-prune_counters (struct gcov_info *gi)
-{
-  for (unsigned i = 0; i < gi->n_functions; i++)
-    {
-      const struct gcov_fn_info *gfi = gi->functions[i];
-      const struct gcov_ctr_info *ci = gfi->ctrs;
-
-      for (unsigned j = 0; j < GCOV_COUNTERS; j++)
-	{
-	  if (gi->merge[j] == NULL)
-	    continue;
-
-	  if (j == GCOV_COUNTER_V_TOPN || j == GCOV_COUNTER_V_INDIR)
-	    {
-	      gcc_assert (!(ci->num % GCOV_TOPN_VALUES_COUNTERS));
-	      for (unsigned k = 0; k < (ci->num / GCOV_TOPN_VALUES_COUNTERS);
-		   k++)
-		{
-		  gcov_type *counters
-		    = ci->values + (k * GCOV_TOPN_VALUES_COUNTERS);
-		  prune_topn_counter (counters + 1, *counters);
-		}
-	    }
-	  ci++;
-	}
-    }
-}
-
 /* This function merges counters in GI_PTR to an existing gcda file.
    Return 0 on success.
    Return -1 on error. In this case, caller will goto read_fatal.  */
@@ -277,12 +262,15 @@ merge_one_data (const char *filename,
   if (!gcov_version (gi_ptr, length, filename))
     return -1;
 
+  /* Skip timestamp.  */
+  gcov_read_unsigned ();
+
   length = gcov_read_unsigned ();
-  if (length != gi_ptr->stamp)
+  if (length != gi_ptr->checksum)
     {
       /* Read from a different compilation.  Overwrite the file.  */
       gcov_error (GCOV_PROF_PREFIX "overwriting an existing profile data "
-		  "with a different timestamp\n", filename);
+		  "with a different checksum\n", filename);
       return 0;
     }
 
@@ -346,16 +334,21 @@ merge_one_data (const char *filename,
           if (!merge)
             continue;
 
-          tag = gcov_read_unsigned ();
-          length = gcov_read_unsigned ();
-          if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
-              || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
-            goto read_mismatch;
-          (*merge) (ci_ptr->values, ci_ptr->num);
-          ci_ptr++;
-        }
+	  tag = gcov_read_unsigned ();
+	  int read_length = (int)gcov_read_unsigned ();
+	  length = abs (read_length);
+	  if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
+	      || (length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num)
+		  && t_ix != GCOV_COUNTER_V_TOPN
+		  && t_ix != GCOV_COUNTER_V_INDIR))
+	    goto read_mismatch;
+	  /* Merging with all zero counters does not make sense.  */
+	  if (read_length > 0)
+	    (*merge) (ci_ptr->values, ci_ptr->num);
+	  ci_ptr++;
+	}
       if ((error = gcov_is_error ()))
-        goto read_error;
+	goto read_error;
     }
 
   if (tag)
@@ -374,31 +367,176 @@ read_error:
   return -1;
 }
 
+/* Write the DATA of LENGTH characters to the gcov file.  */
+
+static void
+gcov_dump_handler (const void *data,
+		   unsigned length,
+		   void *arg ATTRIBUTE_UNUSED)
+{
+  gcov_write (data, length);
+}
+
+/* Allocate SIZE characters and return the address of the allocated memory.  */
+
+static void *
+gcov_allocate_handler (unsigned size, void *arg ATTRIBUTE_UNUSED)
+{
+  return xmalloc (size);
+}
+#endif /* NEED_L_GCOV */
+
+#if defined(NEED_L_GCOV) || defined(NEED_L_GCOV_INFO_TO_GCDA)
+/* Dump the WORD using the DUMP handler called with ARG.  */
+
+static inline void
+dump_unsigned (gcov_unsigned_t word,
+	       void (*dump_fn) (const void *, unsigned, void *),
+	       void *arg)
+{
+  (*dump_fn) (&word, sizeof (word), arg);
+}
+
+/* Dump the COUNTER using the DUMP handler called with ARG.  */
+
+static inline void
+dump_counter (gcov_type counter,
+	      void (*dump_fn) (const void *, unsigned, void *),
+	      void *arg)
+{
+  dump_unsigned ((gcov_unsigned_t)counter, dump_fn, arg);
+
+  if (sizeof (counter) > sizeof (gcov_unsigned_t))
+    dump_unsigned ((gcov_unsigned_t)(counter >> 32), dump_fn, arg);
+  else
+    dump_unsigned (0, dump_fn, arg);
+}
+
+/* Dump the STRING using the DUMP handler called with ARG.  */
+
+static inline void
+ATTRIBUTE_UNUSED
+dump_string (const char *string,
+	     void (*dump_fn) (const void *, unsigned, void *),
+	     void *arg)
+{
+  unsigned length = 0;
+
+  if (string)
+    length = strlen (string) + 1;
+
+  dump_unsigned (length, dump_fn, arg);
+  if (string)
+    (*dump_fn) (string, length, arg);
+}
+
+#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+
+/* Store all TOP N counters where each has a dynamic length.  */
+
+static void
+write_topn_counters (const struct gcov_ctr_info *ci_ptr,
+		     unsigned t_ix,
+		     gcov_unsigned_t n_counts,
+		     void (*dump_fn) (const void *, unsigned, void *),
+		     void *(*allocate_fn)(unsigned, void *),
+		     void *arg)
+{
+  unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
+  gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
+
+  /* It can happen in a multi-threaded environment that number of counters is
+     different from the size of the corresponding linked lists.  */
+#define LIST_SIZE_MIN_LENGTH 4 * 1024
+
+  static unsigned *list_sizes = NULL;
+  static unsigned list_size_length = 0;
+
+  if (list_sizes == NULL || counters > list_size_length)
+    {
+      list_size_length = MAX (LIST_SIZE_MIN_LENGTH, 2 * counters);
+#if !defined(inhibit_libc) && HAVE_SYS_MMAN_H
+      list_sizes
+	= (unsigned *)malloc_mmap (list_size_length * sizeof (unsigned));
+#endif
+
+      /* Malloc fallback.  */
+      if (list_sizes == NULL)
+	list_sizes =
+	  (unsigned *)(*allocate_fn) (list_size_length * sizeof (unsigned),
+				      arg);
+    }
+
+  unsigned pair_total = 0;
+
+  for (unsigned i = 0; i < counters; i++)
+    {
+      gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
+      unsigned sizes = 0;
+
+      for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start;
+	   node != NULL; node = node->next)
+	++sizes;
+
+      pair_total += sizes;
+      list_sizes[i] = sizes;
+    }
+
+  unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total;
+  dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg),
+  dump_unsigned (GCOV_TAG_COUNTER_LENGTH (disk_size), dump_fn, arg);
+
+  for (unsigned i = 0; i < counters; i++)
+    {
+      dump_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i], dump_fn, arg);
+      dump_counter (list_sizes[i], dump_fn, arg);
+      gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
+
+      unsigned j = 0;
+      for (struct gcov_kvp *node = (struct gcov_kvp *)(__INTPTR_TYPE__)start;
+	   j < list_sizes[i]; node = node->next, j++)
+	{
+	  dump_counter (node->value, dump_fn, arg);
+	  dump_counter (node->count, dump_fn, arg);
+	}
+    }
+}
+
 /* Write counters in GI_PTR and the summary in PRG to a gcda file. In
    the case of appending to an existing file, SUMMARY_POS will be non-zero.
    We will write the file starting from SUMMAY_POS.  */
 
 static void
 write_one_data (const struct gcov_info *gi_ptr,
-		const struct gcov_summary *prg_p)
+		const struct gcov_summary *prg_p ATTRIBUTE_UNUSED,
+		void (*dump_fn) (const void *, unsigned, void *),
+		void *(*allocate_fn) (unsigned, void *),
+		void *arg)
 {
   unsigned f_ix;
 
-  gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
-  gcov_write_unsigned (gi_ptr->stamp);
+  dump_unsigned (GCOV_DATA_MAGIC, dump_fn, arg);
+  dump_unsigned (GCOV_VERSION, dump_fn, arg);
+  dump_unsigned (gi_ptr->stamp, dump_fn, arg);
+  dump_unsigned (gi_ptr->checksum, dump_fn, arg);
 
+#ifdef NEED_L_GCOV
   /* Generate whole program statistics.  */
   gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, prg_p);
+#endif
 
   /* Write execution counts for each function.  */
   for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
     {
+#ifdef NEED_L_GCOV
       unsigned buffered = 0;
+#endif
       const struct gcov_fn_info *gfi_ptr;
       const struct gcov_ctr_info *ci_ptr;
       gcov_unsigned_t length;
       unsigned t_ix;
 
+#ifdef NEED_L_GCOV
       if (fn_buffer && fn_buffer->fn_ix == f_ix)
         {
           /* Buffered data from another program.  */
@@ -407,6 +545,7 @@ write_one_data (const struct gcov_info *gi_ptr,
           length = GCOV_TAG_FUNCTION_LENGTH;
         }
       else
+#endif
         {
           gfi_ptr = gi_ptr->functions[f_ix];
           if (gfi_ptr && gfi_ptr->key == gi_ptr)
@@ -415,51 +554,57 @@ write_one_data (const struct gcov_info *gi_ptr,
                 length = 0;
         }
 
-      gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
+      dump_unsigned (GCOV_TAG_FUNCTION, dump_fn, arg);
+      dump_unsigned (length, dump_fn, arg);
       if (!length)
         continue;
 
-      gcov_write_unsigned (gfi_ptr->ident);
-      gcov_write_unsigned (gfi_ptr->lineno_checksum);
-      gcov_write_unsigned (gfi_ptr->cfg_checksum);
+      dump_unsigned (gfi_ptr->ident, dump_fn, arg);
+      dump_unsigned (gfi_ptr->lineno_checksum, dump_fn, arg);
+      dump_unsigned (gfi_ptr->cfg_checksum, dump_fn, arg);
 
       ci_ptr = gfi_ptr->ctrs;
       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
         {
-          gcov_unsigned_t n_counts;
-          gcov_type *c_ptr;
+	  gcov_position_t n_counts;
 
-          if (!gi_ptr->merge[t_ix])
-            continue;
+	  if (!gi_ptr->merge[t_ix])
+	    continue;
 
-          n_counts = ci_ptr->num;
-          gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
-                                 GCOV_TAG_COUNTER_LENGTH (n_counts));
-          c_ptr = ci_ptr->values;
-          while (n_counts--)
-            gcov_write_counter (*c_ptr++);
-          ci_ptr++;
-        }
+	  n_counts = ci_ptr->num;
+
+	  if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR)
+	    write_topn_counters (ci_ptr, t_ix, n_counts, dump_fn, allocate_fn,
+				 arg);
+	  else
+	    {
+	      dump_unsigned (GCOV_TAG_FOR_COUNTER (t_ix), dump_fn, arg);
+	      if (are_all_counters_zero (ci_ptr))
+		/* Do not stream when all counters are zero.  */
+		dump_unsigned (GCOV_TAG_COUNTER_LENGTH (-n_counts),
+			       dump_fn, arg);
+	      else
+		{
+		  dump_unsigned (GCOV_TAG_COUNTER_LENGTH (n_counts),
+				 dump_fn, arg);
+		  for (unsigned i = 0; i < n_counts; i++)
+		    dump_counter (ci_ptr->values[i], dump_fn, arg);
+		}
+	    }
+
+	  ci_ptr++;
+	}
+#ifdef NEED_L_GCOV
       if (buffered)
         fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
+#endif
     }
 
-  gcov_write_unsigned (0);
-}
-
-/* Helper function for merging summary.  */
-
-static void
-merge_summary (int run_counted, struct gcov_summary *summary,
-	      gcov_type run_max)
-{
-  if (!run_counted)
-    {
-      summary->runs++;
-      summary->sum_max += run_max;
-    }
+  dump_unsigned (0, dump_fn, arg);
 }
+#endif /* NEED_L_GCOV || NEED_L_GCOV_INFO_TO_GCDA */
 
+#ifdef NEED_L_GCOV
 /* Dump the coverage counts for one gcov_info object. We merge with existing
    counts when possible, to avoid growing the .da files ad infinitum. We use
    this program's checksum to make sure we only accumulate whole program
@@ -469,17 +614,15 @@ merge_summary (int run_counted, struct gcov_summary *summary,
 
 static void
 dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
-	       unsigned run_counted, gcov_type run_max)
+	       unsigned run_counted ATTRIBUTE_UNUSED,
+	       gcov_type run_max ATTRIBUTE_UNUSED, int mode)
 {
   struct gcov_summary summary = {};
   int error;
   gcov_unsigned_t tag;
   fn_buffer = 0;
 
-  /* Prune current counters before we merge them.  */
-  prune_counters (gi_ptr);
-
-  error = gcov_exit_open_gcda_file (gi_ptr, gf);
+  error = gcov_exit_open_gcda_file (gi_ptr, gf, mode);
   if (error == -1)
     return;
 
@@ -500,9 +643,18 @@ dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
 
   gcov_rewrite ();
 
-  merge_summary (run_counted, &summary, run_max);
+#if !IN_GCOV_TOOL
+  if (!run_counted)
+    {
+      summary.runs++;
+      summary.sum_max += run_max;
+    }
+#else
+  summary = gi_ptr->summary;
+#endif
 
-  write_one_data (gi_ptr, &summary);
+  write_one_data (gi_ptr, &summary, gcov_dump_handler, gcov_allocate_handler,
+		  NULL);
   /* fall through */
 
 read_fatal:;
@@ -510,22 +662,20 @@ read_fatal:;
     fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
 
   if ((error = gcov_close ()))
-    gcov_error (error  < 0 ?
-		GCOV_PROF_PREFIX "Overflow writing\n" :
-		GCOV_PROF_PREFIX "Error writing\n",
-                gf->filename);
+    gcov_error ((error < 0 ? GCOV_PROF_PREFIX "Overflow writing\n"
+		 : GCOV_PROF_PREFIX "Error writing\n"), gf->filename);
 }
 
 
 /* Dump all the coverage counts for the program. It first computes program
    summary and then traverses gcov_list list and dumps the gcov_info
-   objects one by one.  */
+   objects one by one.  Use MODE to open files.  */
 
 #if !IN_GCOV_TOOL
 static
 #endif
 void
-gcov_do_dump (struct gcov_info *list, int run_counted)
+gcov_do_dump (struct gcov_info *list, int run_counted, int mode)
 {
   struct gcov_info *gi_ptr;
   struct gcov_filename gf;
@@ -548,7 +698,7 @@ gcov_do_dump (struct gcov_info *list, int run_counted)
   /* Now merge each file.  */
   for (gi_ptr = list; gi_ptr; gi_ptr = gi_ptr->next)
     {
-      dump_one_gcov (gi_ptr, &gf, run_counted, run_max);
+      dump_one_gcov (gi_ptr, &gf, run_counted, run_max, mode);
       free (gf.filename);
     }
 
@@ -571,7 +721,7 @@ __gcov_dump_one (struct gcov_root *root)
   if (root->dumped)
     return;
 
-  gcov_do_dump (root->list, root->run_counted);
+  gcov_do_dump (root->list, root->run_counted, 0);
   
   root->dumped = 1;
   root->run_counted = 1;
@@ -584,6 +734,15 @@ struct gcov_root __gcov_root;
 struct gcov_master __gcov_master = 
   {GCOV_VERSION, 0};
 
+/* Dynamic pool for gcov_kvp structures.  */
+struct gcov_kvp *__gcov_kvp_dynamic_pool;
+
+/* Index into __gcov_kvp_dynamic_pool array.  */
+unsigned __gcov_kvp_dynamic_pool_index;
+
+/* Size of _gcov_kvp_dynamic_pool array.  */
+unsigned __gcov_kvp_dynamic_pool_size;
+
 void
 __gcov_exit (void)
 {
@@ -625,5 +784,33 @@ __gcov_init (struct gcov_info *info)
     }
 }
 #endif /* !IN_GCOV_TOOL */
-#endif /* L_gcov */
-#endif /* inhibit_libc */
+#endif /* NEED_L_GCOV */
+
+#ifdef NEED_L_GCOV_INFO_TO_GCDA
+/* Convert the gcov info to a gcda data stream.  It is intended for
+   freestanding environments which do not support the C library file I/O.  */
+
+void
+__gcov_info_to_gcda (const struct gcov_info *gi_ptr,
+		     void (*filename_fn) (const char *, void *),
+		     void (*dump_fn) (const void *, unsigned, void *),
+		     void *(*allocate_fn) (unsigned, void *),
+		     void *arg)
+{
+  (*filename_fn) (gi_ptr->filename, arg);
+  write_one_data (gi_ptr, NULL, dump_fn, allocate_fn, arg);
+}
+
+/* Convert the filename to a gcfn data stream.  It is intended for
+   freestanding environments which do not support the C library file I/O.  */
+
+void
+__gcov_filename_to_gcfn (const char *filename,
+			 void (*dump_fn) (const void *, unsigned, void *),
+			 void *arg)
+{
+  dump_unsigned (GCOV_FILENAME_MAGIC, dump_fn, arg);
+  dump_unsigned (GCOV_VERSION, dump_fn, arg);
+  dump_string (filename, dump_fn, arg);
+}
+#endif /* NEED_L_GCOV_INFO_TO_GCDA */
diff --git a/libgcc/libgcov-merge.c b/libgcc/libgcov-merge.c
index c0785b0bf10..1acdaa0403e 100644
--- a/libgcc/libgcov-merge.c
+++ b/libgcc/libgcov-merge.c
@@ -86,86 +86,6 @@ __gcov_merge_time_profile (gcov_type *counters, unsigned n_counters)
 
 #ifdef L_gcov_merge_topn
 
-/* To merging of TOPN profiles.
-   counters[0] is the number of executions
-   for i in 0 ... TOPN-1
-     counters[2 * i + 1] is target
-     counters[2 * i + 2] is corresponding hitrate counter.
-
-   Because we prune counters only those with probability >= 1/TOPN are
-   present now.
-
-   We use sign of counters[0] to track whether the number of different
-   targets exceeds TOPN.  */
-
-static void
-merge_topn_values_set (gcov_type *counters)
-{
-  /* First value is number of total executions of the profiler.  */
-  gcov_type all = gcov_get_counter ();
-  gcov_type *total = &counters[0];
-  ++counters;
-
-  /* Negative value means that counter is missing some of values.  */
-  if (all < 0)
-    *total = -(*total);
-
-  *total += all;
-
-  /* Read all part values.  */
-  gcov_type read_counters[2 * GCOV_TOPN_VALUES];
-  for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
-    {
-      read_counters[2 * i] = gcov_get_counter_target ();
-      read_counters[2 * i + 1] = gcov_get_counter_ignore_scaling (-1);
-    }
-
-  for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
-    {
-      if (read_counters[2 * i + 1] == 0)
-	continue;
-
-      unsigned j;
-      int slot = 0;
-
-      for (j = 0; j < GCOV_TOPN_VALUES; j++)
-	{
-	  if (counters[2 * j] == read_counters[2 * i])
-	    {
-	      counters[2 * j + 1] += read_counters[2 * i + 1];
-	      break;
-	    }
-	  else if (counters[2 * j + 1] < counters[2 * slot + 1])
-	    slot = j;
-	}
-
-      if (j == GCOV_TOPN_VALUES)
-	{
-	  gcov_type slot_count = counters[2 * slot + 1];
-	  /* We found an empty slot.  */
-	  if (slot_count == 0)
-	    {
-	      /* If we found empty slot, add the value.  */
-	      counters[2 * slot] = read_counters[2 * i];
-	      counters[2 * slot + 1] = read_counters[2 * i + 1];
-	    }
-	  else
-	    {
-	      /* Here we are loosing some values.  */
-	      if (*total >= 0)
-		*total = -(*total);
-	      if (read_counters[2 * i + 1] > slot_count)
-		{
-		  counters[2 * slot] = read_counters[2 * i];
-		  counters[2 * slot + 1] = read_counters[2 * i + 1];
-		}
-	      else
-		counters[2 * slot + 1] -= read_counters[2 * i + 1];
-	    }
-	}
-    }
-}
-
 /* The profile merging function for choosing the most common value.
    It is given an array COUNTERS of N_COUNTERS old counters and it
    reads the same number of counters from the gcov file.  The counters
@@ -175,13 +95,30 @@ merge_topn_values_set (gcov_type *counters)
    -- the stored candidate on the most common value of the measured entity
    -- counter
    */
+
 void
 __gcov_merge_topn (gcov_type *counters, unsigned n_counters)
 {
-  gcc_assert (!(n_counters % GCOV_TOPN_VALUES_COUNTERS));
+  gcc_assert (!(n_counters % GCOV_TOPN_MEM_COUNTERS));
 
-  for (unsigned i = 0; i < (n_counters / GCOV_TOPN_VALUES_COUNTERS); i++)
-    merge_topn_values_set (counters + (i * GCOV_TOPN_VALUES_COUNTERS));
+  for (unsigned i = 0; i < (n_counters / GCOV_TOPN_MEM_COUNTERS); i++)
+    {
+      /* First value is number of total executions of the profiler.  */
+      gcov_type all = gcov_get_counter_ignore_scaling (-1);
+      gcov_type n = gcov_get_counter_ignore_scaling (-1);
+
+      counters[GCOV_TOPN_MEM_COUNTERS * i] += all;
+
+      for (unsigned j = 0; j < n; j++)
+	{
+	  gcov_type value = gcov_get_counter_target ();
+	  gcov_type count = gcov_get_counter_ignore_scaling (-1);
+
+	  // TODO: we should use atomic here
+	  gcov_topn_add_value (counters + GCOV_TOPN_MEM_COUNTERS * i, value,
+			       count, 0, 0);
+	}
+    }
 }
 #endif /* L_gcov_merge_topn */
 
diff --git a/libgcc/libgcov-profiler.c b/libgcc/libgcov-profiler.c
index 6043ac4c7a1..7b171382a07 100644
--- a/libgcc/libgcov-profiler.c
+++ b/libgcc/libgcov-profiler.c
@@ -105,51 +105,13 @@ __gcov_pow2_profiler_atomic (gcov_type *counters, gcov_type value)
 }
 #endif
 
-
 /* Tries to determine N most commons value among its inputs.  */
 
 static inline void
 __gcov_topn_values_profiler_body (gcov_type *counters, gcov_type value,
 				  int use_atomic)
 {
-  if (use_atomic)
-    __atomic_fetch_add (&counters[0], 1, __ATOMIC_RELAXED);
-  else
-    counters[0]++;
-
-  ++counters;
-
-  /* First try to find an existing value.  */
-  int empty_counter = -1;
-
-  for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
-    if (value == counters[2 * i])
-      {
-	if (use_atomic)
-	  __atomic_fetch_add (&counters[2 * i + 1], GCOV_TOPN_VALUES,
-			      __ATOMIC_RELAXED);
-	else
-	  counters[2 * i + 1] += GCOV_TOPN_VALUES;
-	return;
-      }
-    else if (counters[2 * i + 1] <= 0)
-      empty_counter = i;
-
-  /* Find an empty slot for a new value.  */
-  if (empty_counter != -1)
-    {
-      counters[2 * empty_counter] = value;
-      counters[2 * empty_counter + 1] = GCOV_TOPN_VALUES;
-      return;
-    }
-
-  /* We haven't found an empty slot, then decrement all
-     counter values by one.  */
-  for (unsigned i = 0; i < GCOV_TOPN_VALUES; i++)
-    if (use_atomic)
-      __atomic_fetch_sub (&counters[2 * i + 1], 1, __ATOMIC_RELAXED);
-    else
-      counters[2 * i + 1]--;
+  gcov_topn_add_value (counters, value, 1, use_atomic, 1);
 }
 
 #ifdef L_gcov_topn_values_profiler
diff --git a/libgcc/libgcov-util.c b/libgcc/libgcov-util.c
index fff54c6a3f6..73dacf795a0 100644
--- a/libgcc/libgcov-util.c
+++ b/libgcc/libgcov-util.c
@@ -57,12 +57,12 @@ void gcov_set_verbose (void)
 #include <ftw.h>
 #endif
 
-static void tag_function (unsigned, unsigned);
-static void tag_blocks (unsigned, unsigned);
-static void tag_arcs (unsigned, unsigned);
-static void tag_lines (unsigned, unsigned);
-static void tag_counters (unsigned, unsigned);
-static void tag_summary (unsigned, unsigned);
+static void tag_function (unsigned, int);
+static void tag_blocks (unsigned, int);
+static void tag_arcs (unsigned, int);
+static void tag_lines (unsigned, int);
+static void tag_counters (unsigned, int);
+static void tag_summary (unsigned, int);
 
 /* The gcov_info for the first module.  */
 static struct gcov_info *curr_gcov_info;
@@ -80,8 +80,6 @@ static int k_ctrs_mask[GCOV_COUNTERS];
 static struct gcov_ctr_info k_ctrs[GCOV_COUNTERS];
 /* Number of kind of counters that have been seen.  */
 static int k_ctrs_types;
-/* The object summary being processed.  */
-static struct gcov_summary *curr_object_summary;
 
 /* Merge functions for counters.  */
 #define DEF_GCOV_COUNTER(COUNTER, NAME, FN_TYPE) __gcov_merge ## FN_TYPE,
@@ -119,7 +117,7 @@ typedef struct tag_format
 {
     unsigned tag;
     char const *name;
-    void (*proc) (unsigned, unsigned);
+    void (*proc) (unsigned, int);
 } tag_format_t;
 
 /* Handler table for various Tags.  */
@@ -140,7 +138,7 @@ static const tag_format_t tag_table[] =
 /* Handler for reading function tag.  */
 
 static void
-tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_function (unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED)
 {
   int i;
 
@@ -173,7 +171,7 @@ tag_function (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
 /* Handler for reading block tag.  */
 
 static void
-tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_blocks (unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED)
 {
   /* TBD: gcov-tool currently does not handle gcno files. Assert here.  */
   gcc_unreachable ();
@@ -182,7 +180,7 @@ tag_blocks (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
 /* Handler for reading flow arc tag.  */
 
 static void
-tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_arcs (unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED)
 {
   /* TBD: gcov-tool currently does not handle gcno files. Assert here.  */
   gcc_unreachable ();
@@ -191,7 +189,7 @@ tag_arcs (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
 /* Handler for reading line tag.  */
 
 static void
-tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_lines (unsigned tag ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED)
 {
   /* TBD: gcov-tool currently does not handle gcno files. Assert here.  */
   gcc_unreachable ();
@@ -200,9 +198,9 @@ tag_lines (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
 /* Handler for reading counters array tag with value as TAG and length of LENGTH.  */
 
 static void
-tag_counters (unsigned tag, unsigned length)
+tag_counters (unsigned tag, int length)
 {
-  unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+  unsigned n_counts = GCOV_TAG_COUNTER_NUM (abs (length));
   gcov_type *values;
   unsigned ix;
   unsigned tag_ix;
@@ -213,20 +211,21 @@ tag_counters (unsigned tag, unsigned length)
   gcc_assert (k_ctrs[tag_ix].num == 0);
   k_ctrs[tag_ix].num = n_counts;
 
-  k_ctrs[tag_ix].values = values = (gcov_type *) xmalloc (n_counts * sizeof (gcov_type));
+  k_ctrs[tag_ix].values = values = (gcov_type *) xcalloc (sizeof (gcov_type),
+							  n_counts);
   gcc_assert (values);
 
-  for (ix = 0; ix != n_counts; ix++)
-    values[ix] = gcov_read_counter ();
+  if (length > 0)
+    for (ix = 0; ix != n_counts; ix++)
+      values[ix] = gcov_read_counter ();
 }
 
 /* Handler for reading summary tag.  */
 
 static void
-tag_summary (unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED)
+tag_summary (unsigned tag ATTRIBUTE_UNUSED, int ATTRIBUTE_UNUSED)
 {
-  curr_object_summary = (gcov_summary *) xcalloc (sizeof (gcov_summary), 1);
-  gcov_read_summary (curr_object_summary);
+  gcov_read_summary (&curr_gcov_info->summary);
 }
 
 /* This function is called at the end of reading a gcda file.
@@ -269,17 +268,10 @@ read_gcda_file (const char *filename)
     k_ctrs_mask[i] = 0;
   k_ctrs_types = 0;
 
-  if (!gcov_open (filename))
-    {
-      fnotice (stderr, "%s:cannot open\n", filename);
-      return NULL;
-    }
-
   /* Read magic.  */
   if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
     {
       fnotice (stderr, "%s:not a gcov data file\n", filename);
-      gcov_close ();
       return NULL;
     }
 
@@ -288,7 +280,6 @@ read_gcda_file (const char *filename)
   if (version != GCOV_VERSION)
     {
       fnotice (stderr, "%s:incorrect gcov version %d vs %d \n", filename, version, GCOV_VERSION);
-      gcov_close ();
       return NULL;
     }
 
@@ -297,21 +288,21 @@ read_gcda_file (const char *filename)
              sizeof (struct gcov_ctr_info) * GCOV_COUNTERS, 1);
 
   obj_info->version = version;
+  obj_info->filename = filename;
   obstack_init (&fn_info);
   num_fn_info = 0;
   curr_fn_info = 0;
-  curr_object_summary = NULL;
-  {
-    size_t len = strlen (filename) + 1;
-    char *str_dup = (char*) xmalloc (len);
 
-    memcpy (str_dup, filename, len);
-    obj_info->filename = str_dup;
-  }
+  /* Prepend to global gcov info list.  */
+  obj_info->next = gcov_info_head;
+  gcov_info_head = obj_info;
 
   /* Read stamp.  */
   obj_info->stamp = gcov_read_unsigned ();
 
+  /* Read checksum.  */
+  obj_info->checksum = gcov_read_unsigned ();
+
   while (1)
     {
       gcov_position_t base;
@@ -324,7 +315,8 @@ read_gcda_file (const char *filename)
       tag = gcov_read_unsigned ();
       if (!tag)
         break;
-      length = gcov_read_unsigned ();
+      int read_length = (int)gcov_read_unsigned ();
+      length = read_length > 0 ? read_length : 0;
       base = gcov_position ();
       mask = GCOV_TAG_MASK (tag) >> 1;
       for (tag_depth = 4; mask; mask >>= 8)
@@ -357,7 +349,7 @@ read_gcda_file (const char *filename)
         {
           unsigned long actual_length;
 
-          (*format->proc) (tag, length);
+	  (*format->proc) (tag, read_length);
 
           actual_length = gcov_position () - base;
           if (actual_length > length)
@@ -379,7 +371,6 @@ read_gcda_file (const char *filename)
     }
 
   read_gcda_finalize (obj_info);
-  gcov_close ();
 
   return obj_info;
 }
@@ -393,9 +384,8 @@ ftw_read_file (const char *filename,
                const struct stat *status ATTRIBUTE_UNUSED,
                int type)
 {
-  int filename_len;
-  int suffix_len;
-  struct gcov_info *obj_info;
+  size_t filename_len;
+  size_t suffix_len;
 
   /* Only read regular files.  */
   if (type != FTW_F)
@@ -413,12 +403,14 @@ ftw_read_file (const char *filename,
   if (verbose)
     fnotice (stderr, "reading file: %s\n", filename);
 
-  obj_info = read_gcda_file (filename);
-  if (!obj_info)
-    return 0;
+  if (!gcov_open (filename, 1))
+    {
+      fnotice (stderr, "%s:cannot open:%s\n", filename, xstrerror (errno));
+      return 0;
+    }
 
-  obj_info->next = gcov_info_head;
-  gcov_info_head = obj_info;
+  (void)read_gcda_file (xstrdup (filename));
+  gcov_close ();
 
   return 0;
 }
@@ -540,6 +532,11 @@ gcov_merge (struct gcov_info *info1, struct gcov_info *info2, int w)
   int has_mismatch = 0;
 
   gcc_assert (info2->n_functions == n_functions);
+
+  /* Merge summary.  */
+  info1->summary.runs += info2->summary.runs;
+  info1->summary.sum_max += info2->summary.sum_max;
+
   for (f_ix = 0; f_ix < n_functions; f_ix++)
     {
       unsigned t_ix;
@@ -617,16 +614,16 @@ find_match_gcov_info (struct gcov_info **array, int size,
 }
 
 /* Merge the list of gcov_info objects from SRC_PROFILE to TGT_PROFILE.
-   Return 0 on success: without mismatch.
-   Reutrn 1 on error.  */
+   Return the list of merged gcov_info objects.  Return NULL if the list is
+   empty.  */
 
-int
+struct gcov_info *
 gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile,
                     int w1, int w2)
 {
   struct gcov_info *gi_ptr;
   struct gcov_info **tgt_infos;
-  struct gcov_info *tgt_tail;
+  struct gcov_info **tgt_tail;
   struct gcov_info **in_src_not_tgt;
   unsigned tgt_cnt = 0, src_cnt = 0;
   unsigned unmatch_info_cnt = 0;
@@ -646,7 +643,10 @@ gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile
   for (gi_ptr = tgt_profile, i = 0; gi_ptr; gi_ptr = gi_ptr->next, i++)
     tgt_infos[i] = gi_ptr;
 
-  tgt_tail = tgt_infos[tgt_cnt - 1];
+  if (tgt_cnt)
+     tgt_tail = &tgt_infos[tgt_cnt - 1]->next;
+  else
+     tgt_tail = &tgt_profile;
 
   /* First pass on tgt_profile, we multiply w1 to all counters.  */
   if (w1 > 1)
@@ -675,14 +675,109 @@ gcov_profile_merge (struct gcov_info *tgt_profile, struct gcov_info *src_profile
       gi_ptr = in_src_not_tgt[i];
       gcov_merge (gi_ptr, gi_ptr, w2 - 1);
       gi_ptr->next = NULL;
-      tgt_tail->next = gi_ptr;
-      tgt_tail = gi_ptr;
+      *tgt_tail = gi_ptr;
+      tgt_tail = &gi_ptr->next;
     }
 
   free (in_src_not_tgt);
   free (tgt_infos);
 
-  return 0;
+  return tgt_profile;
+}
+
+/* Deserialize gcov_info objects and associated filenames from the file
+   specified by FILENAME to create a profile list.  When FILENAME is NULL, read
+   from stdin.  Return the profile list.  */
+
+struct gcov_info *
+deserialize_profiles (const char *filename)
+{
+  read_profile_dir_init ();
+
+  while (true)
+    {
+      unsigned version;
+      const char *filename_of_info;
+      struct gcov_info *obj_info;
+
+      if (!gcov_magic (gcov_read_unsigned (), GCOV_FILENAME_MAGIC))
+	{
+	  if (gcov_is_error () != 2)
+	    fnotice (stderr, "%s:not a gcfn stream\n", filename);
+	  break;
+	}
+
+      version = gcov_read_unsigned ();
+      if (version != GCOV_VERSION)
+	{
+	  fnotice (stderr, "%s:incorrect gcov version %d vs %d \n",
+		   filename, version, GCOV_VERSION);
+	  break;
+	}
+
+      filename_of_info = gcov_read_string ();
+      if (!filename_of_info)
+	{
+	  fnotice (stderr, "%s:no filename in gcfn stream\n",
+		   filename);
+	  break;
+	}
+
+      obj_info = read_gcda_file (filename);
+      if (!obj_info)
+	break;
+
+      obj_info->filename = filename_of_info;
+    }
+
+  return gcov_info_head;
+}
+
+/* For each profile of the list specified by SRC_PROFILE, read the GCDA file of
+   the profile.  If a GCDA file exists, add the profile to a list.  Return the
+   profile list.  */
+
+struct gcov_info *
+get_target_profiles_for_merge (struct gcov_info *src_profile)
+{
+  struct gcov_info *gi_ptr;
+
+  read_profile_dir_init ();
+
+  for (gi_ptr = src_profile; gi_ptr; gi_ptr = gi_ptr->next)
+    if (gcov_open (gi_ptr->filename, 1))
+      {
+	(void)read_gcda_file (gi_ptr->filename);
+	gcov_close ();
+      }
+
+  return gcov_info_head;
+}
+
+/* Deserialize gcov_info objects and associated filenames from the file
+   specified by FILENAME to create a source profile list.  When FILENAME is
+   NULL, read from stdin.  Use the filenames of the source profile list to get
+   a target profile list.  Merge the source profile list into the target
+   profile list using weights W1 and W2.  Return the list of merged gcov_info
+   objects.  Return NULL if the list is empty.  */
+
+struct gcov_info *
+gcov_profile_merge_stream (const char *filename, int w1, int w2)
+{
+  struct gcov_info *tgt_profile;
+  struct gcov_info *src_profile;
+
+  if (!gcov_open (filename, 1))
+    {
+      fnotice (stderr, "%s:cannot open:%s\n", filename, xstrerror (errno));
+      return NULL;
+    }
+
+  src_profile = deserialize_profiles (filename ? filename : "<stdin>");
+  gcov_close ();
+  tgt_profile = get_target_profiles_for_merge (src_profile);
+
+  return gcov_profile_merge (tgt_profile, src_profile, w1, w2);
 }
 
 typedef gcov_type (*counter_op_fn) (gcov_type, void*, void*);
diff --git a/libgcc/libgcov.h b/libgcc/libgcov.h
index 2ba2f931e4c..146ccabec08 100644
--- a/libgcc/libgcov.h
+++ b/libgcc/libgcov.h
@@ -45,10 +45,14 @@
 #include "libgcc_tm.h"
 #include "gcov.h"
 
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
 #if __CHAR_BIT__ == 8
 typedef unsigned gcov_unsigned_t __attribute__ ((mode (SI)));
 typedef unsigned gcov_position_t __attribute__ ((mode (SI)));
-#if LONG_LONG_TYPE_SIZE > 32
+#if GCOV_TYPE_SIZE > 32
 typedef signed gcov_type __attribute__ ((mode (DI)));
 typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI)));
 #else
@@ -59,7 +63,7 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
 #if __CHAR_BIT__ == 16
 typedef unsigned gcov_unsigned_t __attribute__ ((mode (HI)));
 typedef unsigned gcov_position_t __attribute__ ((mode (HI)));
-#if LONG_LONG_TYPE_SIZE > 32
+#if GCOV_TYPE_SIZE > 32
 typedef signed gcov_type __attribute__ ((mode (SI)));
 typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI)));
 #else
@@ -69,7 +73,7 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
 #else
 typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI)));
 typedef unsigned gcov_position_t __attribute__ ((mode (QI)));
-#if LONG_LONG_TYPE_SIZE > 32
+#if GCOV_TYPE_SIZE > 32
 typedef signed gcov_type __attribute__ ((mode (HI)));
 typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI)));
 #else
@@ -85,19 +89,29 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
 #define GCOV_LOCKED 0
 #endif
 
+#ifndef GCOV_SUPPORTS_ATOMIC
+/* Detect whether target can support atomic update of profilers.  */
+#if __SIZEOF_LONG_LONG__ == 4 && __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4
+#define GCOV_SUPPORTS_ATOMIC 1
+#else
+#if __SIZEOF_LONG_LONG__ == 8 && __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8
+#define GCOV_SUPPORTS_ATOMIC 1
+#else
+#define GCOV_SUPPORTS_ATOMIC 0
+#endif
+#endif
+#endif
+
 /* In libgcov we need these functions to be extern, so prefix them with
    __gcov.  In libgcov they must also be hidden so that the instance in
    the executable is not also used in a DSO.  */
 #define gcov_var __gcov_var
 #define gcov_open __gcov_open
 #define gcov_close __gcov_close
-#define gcov_write_tag_length __gcov_write_tag_length
 #define gcov_position __gcov_position
-#define gcov_seek __gcov_seek
 #define gcov_rewrite __gcov_rewrite
 #define gcov_is_error __gcov_is_error
 #define gcov_write_unsigned __gcov_write_unsigned
-#define gcov_write_counter __gcov_write_counter
 #define gcov_write_summary __gcov_write_summary
 #define gcov_read_unsigned __gcov_read_unsigned
 #define gcov_read_counter __gcov_read_counter
@@ -116,12 +130,19 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
 typedef unsigned gcov_unsigned_t;
 typedef unsigned gcov_position_t;
 /* gcov_type is typedef'd elsewhere for the compiler */
+
 #if defined (HOST_HAS_F_SETLKW)
 #define GCOV_LOCKED 1
 #else
 #define GCOV_LOCKED 0
 #endif
 
+#if defined (HOST_HAS_LK_LOCK)
+#define GCOV_LOCKED_WITH_LOCKING 1
+#else
+#define GCOV_LOCKED_WITH_LOCKING 0
+#endif
+
 /* Some Macros specific to gcov-tool.  */
 
 #define L_gcov 1
@@ -155,6 +176,16 @@ extern struct gcov_info *gcov_list;
 #define ATTRIBUTE_HIDDEN
 #endif
 
+#if HAVE_SYS_MMAN_H
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+#if !defined (MAP_ANONYMOUS) && defined (MAP_ANON)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+#endif
+
 #include "gcov-io.h"
 
 /* Structures embedded in coveraged program.  The structures generated
@@ -192,6 +223,7 @@ struct gcov_info
   struct gcov_info *next;	/* link to next, used by libgcov */
 
   gcov_unsigned_t stamp;	/* uniquifying time stamp */
+  gcov_unsigned_t checksum;	/* unique object checksum */
   const char *filename;		/* output file name */
 
   gcov_merge_fn merge[GCOV_COUNTERS];  /* merge functions (null for
@@ -204,6 +236,7 @@ struct gcov_info
                                                   to function information  */
 #else
   const struct gcov_fn_info **functions;
+  struct gcov_summary summary;
 #endif /* !IN_GCOV_TOOL */
 };
 
@@ -236,6 +269,9 @@ struct indirect_call_tuple
   
 /* Exactly one of these will be active in the process.  */
 extern struct gcov_master __gcov_master;
+extern struct gcov_kvp *__gcov_kvp_dynamic_pool;
+extern unsigned __gcov_kvp_dynamic_pool_index;
+extern unsigned __gcov_kvp_dynamic_pool_size;
 
 /* Dump a set of gcov objects.  */
 extern void __gcov_dump_one (struct gcov_root *) ATTRIBUTE_HIDDEN;
@@ -294,14 +330,9 @@ extern int __gcov_execve (const char *, char  *const [], char *const [])
   ATTRIBUTE_HIDDEN;
 
 /* Functions that only available in libgcov.  */
-GCOV_LINKAGE int gcov_open (const char */*name*/) ATTRIBUTE_HIDDEN;
-GCOV_LINKAGE void gcov_write_counter (gcov_type) ATTRIBUTE_HIDDEN;
-GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t, gcov_unsigned_t)
-    ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
                                       const struct gcov_summary *)
     ATTRIBUTE_HIDDEN;
-GCOV_LINKAGE void gcov_seek (gcov_position_t /*position*/) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_rewrite (void) ATTRIBUTE_HIDDEN;
 
 /* "Counts" stored in gcda files can be a real counter value, or
@@ -367,6 +398,161 @@ gcov_get_counter_target (void)
 #endif
 }
 
+/* Add VALUE to *COUNTER and make it with atomic operation
+   if USE_ATOMIC is true.  */
+
+static inline void
+gcov_counter_add (gcov_type *counter, gcov_type value, int use_atomic)
+{
+  if (use_atomic)
+    __atomic_fetch_add (counter, value, __ATOMIC_RELAXED);
+  else
+    *counter += value;
+}
+
+#if HAVE_SYS_MMAN_H
+
+/* Allocate LENGTH with mmap function.  */
+
+static inline void *
+malloc_mmap (size_t length)
+{
+  return mmap (NULL, length, PROT_READ | PROT_WRITE,
+	       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+}
+
+#endif
+
+/* Allocate gcov_kvp from statically pre-allocated pool,
+   or use heap otherwise.  */
+
+static inline struct gcov_kvp *
+allocate_gcov_kvp (void)
+{
+#define MMAP_CHUNK_SIZE	(128 * 1024)
+  struct gcov_kvp *new_node = NULL;
+  unsigned kvp_sizeof = sizeof(struct gcov_kvp);
+
+  /* Try mmaped pool if available.  */
+#if !defined(IN_GCOV_TOOL) && !defined(L_gcov_merge_topn) && HAVE_SYS_MMAN_H
+  if (__gcov_kvp_dynamic_pool == NULL
+      || __gcov_kvp_dynamic_pool_index >= __gcov_kvp_dynamic_pool_size)
+    {
+      void *ptr = malloc_mmap (MMAP_CHUNK_SIZE);
+      if (ptr != MAP_FAILED)
+	{
+	  __gcov_kvp_dynamic_pool = ptr;
+	  __gcov_kvp_dynamic_pool_size = MMAP_CHUNK_SIZE / kvp_sizeof;
+	  __gcov_kvp_dynamic_pool_index = 0;
+	}
+    }
+
+  if (__gcov_kvp_dynamic_pool != NULL)
+    {
+      unsigned index;
+#if GCOV_SUPPORTS_ATOMIC
+      index
+	= __atomic_fetch_add (&__gcov_kvp_dynamic_pool_index, 1,
+			      __ATOMIC_RELAXED);
+#else
+      index = __gcov_kvp_dynamic_pool_index++;
+#endif
+      if (index < __gcov_kvp_dynamic_pool_size)
+	new_node = __gcov_kvp_dynamic_pool + index;
+    }
+#endif
+
+  /* Fallback to malloc.  */
+  if (new_node == NULL)
+    new_node = (struct gcov_kvp *)xcalloc (1, kvp_sizeof);
+
+  return new_node;
+}
+
+/* Add key value pair VALUE:COUNT to a top N COUNTERS.  When INCREMENT_TOTAL
+   is true, add COUNT to total of the TOP counter.  If USE_ATOMIC is true,
+   do it in atomic way.  */
+
+static inline void
+gcov_topn_add_value (gcov_type *counters, gcov_type value, gcov_type count,
+		     int use_atomic, int increment_total)
+{
+  if (increment_total)
+    gcov_counter_add (&counters[0], 1, use_atomic);
+
+  struct gcov_kvp *prev_node = NULL;
+  struct gcov_kvp *minimal_node = NULL;
+  struct gcov_kvp *current_node  = (struct gcov_kvp *)(intptr_t)counters[2];
+
+  while (current_node)
+    {
+      if (current_node->value == value)
+	{
+	  gcov_counter_add (&current_node->count, count, use_atomic);
+	  return;
+	}
+
+      if (minimal_node == NULL
+	  || current_node->count < minimal_node->count)
+	minimal_node = current_node;
+
+      prev_node = current_node;
+      current_node = current_node->next;
+    }
+
+  if (counters[1] == GCOV_TOPN_MAXIMUM_TRACKED_VALUES)
+    {
+      if (--minimal_node->count < count)
+	{
+	  minimal_node->value = value;
+	  minimal_node->count = count;
+	}
+    }
+  else
+    {
+      struct gcov_kvp *new_node = allocate_gcov_kvp ();
+      if (new_node == NULL)
+	return;
+
+      new_node->value = value;
+      new_node->count = count;
+
+      int success = 0;
+      if (!counters[2])
+	{
+#if GCOV_SUPPORTS_ATOMIC
+	  if (use_atomic)
+	    {
+	      struct gcov_kvp **ptr = (struct gcov_kvp **)(intptr_t)&counters[2];
+	      success = !__sync_val_compare_and_swap (ptr, 0, new_node);
+	    }
+	  else
+#endif
+	    {
+	      counters[2] = (intptr_t)new_node;
+	      success = 1;
+	    }
+	}
+      else if (prev_node && !prev_node->next)
+	{
+#if GCOV_SUPPORTS_ATOMIC
+	  if (use_atomic)
+	    success = !__sync_val_compare_and_swap (&prev_node->next, 0,
+						    new_node);
+	  else
+#endif
+	    {
+	      prev_node->next = new_node;
+	      success = 1;
+	    }
+	}
+
+      /* Increment number of nodes.  */
+      if (success)
+	gcov_counter_add (&counters[1], 1, use_atomic);
+    }
+}
+
 #endif /* !inhibit_libc */
 
 #endif /* GCC_LIBGCOV_H */
diff --git a/libgcc/unwind-arm-common.inc b/libgcc/unwind-arm-common.inc
index 31a072b9b9b..6681cbcdcb0 100644
--- a/libgcc/unwind-arm-common.inc
+++ b/libgcc/unwind-arm-common.inc
@@ -50,10 +50,6 @@
 #define ARM_SIGCONTEXT_R0		0xc
 #endif
 
-/* We add a prototype for abort here to avoid creating a dependency on
-   target headers.  */
-extern void abort (void);
-
 /* Definitions for C++ runtime support routines.  We make these weak
    declarations to avoid pulling in libsupc++ unnecessarily.  */
 typedef unsigned char bool;
-- 
2.35.3

