diff mbox

[Google/4-8] Support for user-guided feedback-directed library optimization

Message ID CAAe5K+Wtpkkkfhnt8q_PgBg3EgAHwrM7oGg0HEaNbnqC+R0Mbw@mail.gmail.com
State New
Headers show

Commit Message

Teresa Johnson May 20, 2014, 4:09 a.m. UTC
On Sun, May 11, 2014 at 11:33 AM, Xinliang David Li <davidxl@google.com> wrote:
>> ===================================================================
>> --- gcc/builtins.c    (revision 210019)
>> +++ gcc/builtins.c    (working copy)
>> @@ -87,6 +87,9 @@ static rtx result_vector (int, rtx);
>>  #endif
>>  static void expand_builtin_update_setjmp_buf (rtx);
>>  static void expand_builtin_prefetch (tree);
>> +static rtx expand_builtin_profile_invoke (tree);
>> +static rtx expand_builtin_profile_register_handler (tree);
>
> It might be more general to support handler invocation with some data
> -- basically let handler to take a 'void*' as type of the data.

As discussed offline, deferring to a follow-on patch.

>
>
>> +/* Expand a call to __builtin_profile_invoke.  Passed a pointer to
>> +   a routine that should be called on -fprofile-generate compiles
>> +   and the profile counter address and data to pass to the routine.  */
>> +
>> +static rtx
>> +expand_builtin_profile_invoke (tree exp)
>> +{
>> +  tree func, ctr, data, fndecl;
>
> Expanding late is ok for now. It might be better to support expansion
> of builin_invoke before ipa inlining (after early inline). This may
> allow more efficient code gen (i.e., when the update function is
> defined inline ..)

Ditto.

>
>> +
>> +/* Expand a call to __builtin_profile_register_handler.  Passed a pointer to
>> +   a routine that should be called atexit in -fprofile-generate binaries.  */
>> +
>> +static rtx
>> +expand_builtin_profile_register_handler (tree exp)
>> +{
>> +  tree fn, fndecl;
>> +
>
> See comment about supporting one parameter.

Ditto.

>
>> +  gcc_assert (alloc < GCOV_BLOCK_SIZE);
>> +  return alloc;
>> +}
>
>>  #if !IN_GCOV
>>  /* Write out the current block, if needs be.  */
>
>>
>> @@ -292,12 +308,11 @@ gcov_write_words (unsigned words)
>>  #if IN_LIBGCOV
>>    if (gcov_var.offset >= GCOV_BLOCK_SIZE)
>>      {
>> -      gcov_write_block (GCOV_BLOCK_SIZE);
>> -      if (gcov_var.offset)
>> -     {
>> -       gcc_assert (gcov_var.offset == 1);
>> -       memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4);
>> -     }
>> +      // Strings will cause offset to go above GCOV_BLOCK_SIZE
>> +      // by a variable amount, so we no longer assert that any
>> +      // offset be 1.
>> +      gcov_write_block (gcov_var.offset);
>> +      gcc_assert (!gcov_var.offset);
>
> This part needs more explanation.

After looking at this again, I realized I was papering over a big
issue when enabling string writing from libgcov, involving buffer
overflow, since in the libgcov case buffer is statically sized to
GCOV_BLOCK_SIZE + 1. gcov_write_block was previously only called with
"words" equal to 1 or 2, and so the offset would only exceed the block
size by at most 1. With strings we may exceed it by a variable amount,
which is obviously bad when buffer is a static size as in libgcov. I
reverted this change, and instead added handling to gcov_write_string,
to check if the buffer size would be exceeded by the new string and if
so first call gcov_write_block to open up space for the string (which
is checked to ensure it doesn't exceed the block size).

>
>> +/* Write parameter values to the gcov file. These should be applied
>> +   to profile-use compiles as macro definitions by the compiler.  */
>> +
>> +GCOV_LINKAGE void
>> +gcov_write_parameters (struct gcov_parameter_value *parameters)
>> +{
>> +  gcov_unsigned_t len = 0;
>> +  struct gcov_parameter_value *curr_parm;
>> +
>> +  if (!parameters)
>> +    return;
>> +
>> +  for (curr_parm = parameters; curr_parm;
>> +       curr_parm = curr_parm->next)
>> +    {
>
> Maybe comment here -- string followed by 2 word counter value.

Done.

>
>> +  struct gcov_parameter_value *cur_new_parm, *cur_merge_parm, *merged_parms;
>> +
>> +  merged_parms = saved_parms;
>> +
>> +  for (cur_new_parm = new_parms; cur_new_parm;
>> +       cur_new_parm = cur_new_parm->next)
>> +    {
>> +      cur_merge_parm = find_parameter (saved_parms, cur_new_parm->macro_name);
>> +
>> +      if (cur_merge_parm)
>> +        {
>> +          // Simply average them for now. The best merge strategy will
>> +          // depend on how they were computed in the first place.
>> +          cur_merge_parm->value
>> +              = (cur_merge_parm->value + cur_new_parm->value) / 2;
>
> Some parmeters may have value set that is not averageable. When
> recording parameters, maybe record the parmeter type of some sort (by
> default it is avaragble).

Deferred and added a comment.

>
>
>> +                      struct gcov_parameter_value **merged_parameters)
>>  {
>>    gcov_unsigned_t tag, length, version, stamp;
>>    unsigned t_ix, f_ix;
>> @@ -418,6 +490,15 @@ gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
>>      next_summary:;
>>      }
>>
>> +  if (tag == GCOV_TAG_PARAMETERS)
>> +    {
>> +      length = gcov_read_unsigned ();
>> +      *merged_parameters = gcov_read_parameters (length);
>> +      *merged_parameters = merge_parameters (*merged_parameters,
>> +                                             gcov_parameter_values);
>> +    }
>> +  tag = gcov_read_unsigned ();
>> +
>
> Should the tag reading be inside the previous guard?

Yes, this was a bug. Ran the new patch through a profiledbootstrap as
well, which presumably would have caught this.

New patch attached. Passes regular regression tests and
profiledbootstrap+regression tests.

Thanks,
Teresa

>
> David
>
>
>
>      81,1          98%
>
>
>
> On Fri, May 9, 2014 at 1:50 PM, Teresa Johnson <tejohnson@google.com> wrote:
>> The attached patch adds support for user-guided feedback-directed
>> library optimization, for google/gcc-4_8 initially (to be ported to
>> google/gcc-4_9).
>>
>> Cc'ing Honza for any comments since I would eventually like to send
>> this and follow-on work to trunk.
>>
>> Patch to add support for user-guided feedback-directed library optimization.
>>
>> Contains support for builtins and attributes for specifying user-supplied
>> routines for initializing, updating and analyzing profile counters during
>> profile collection runs, and for feeding back macro values via a new
>> parameter section in the gcda file.
>>
>> Passes regression tests. Ok for google branches?
>>
>> Thanks,
>> Teresa
>>
>> --
>> Teresa Johnson | Software Engineer | tejohnson@google.com | 408-460-2413
diff mbox

Patch

Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 210624)
+++ gcc/builtins.c	(working copy)
@@ -87,6 +87,9 @@  static rtx result_vector (int, rtx);
 #endif
 static void expand_builtin_update_setjmp_buf (rtx);
 static void expand_builtin_prefetch (tree);
+static rtx expand_builtin_profile_invoke (tree);
+static rtx expand_builtin_profile_register_handler (tree);
+static rtx expand_builtin_profile_record_parameter (tree);
 static rtx expand_builtin_apply_args (void);
 static rtx expand_builtin_apply_args_1 (void);
 static rtx expand_builtin_apply (rtx, rtx, rtx);
@@ -222,6 +225,7 @@  static tree do_mpfr_bessel_n (tree, tree, tree,
 static tree do_mpfr_remquo (tree, tree, tree);
 static tree do_mpfr_lgamma_r (tree, tree, tree);
 static void expand_builtin_sync_synchronize (void);
+static tree build_call_nofold_loc (location_t loc, tree fndecl, int n, ...);
 
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
@@ -1223,6 +1227,94 @@  expand_builtin_prefetch (tree exp)
     emit_insn (op0);
 }
 
+/* Expand a call to __builtin_profile_invoke.  Passed a pointer to
+   a routine that should be called on -fprofile-generate compiles
+   and the profile counter address and data to pass to the routine.  */
+
+static rtx
+expand_builtin_profile_invoke (tree exp)
+{
+  tree func, ctr, data, fndecl;
+  tree call;
+  location_t loc = EXPR_LOCATION (exp);
+
+  /* This is only relevent to -fprofile-generate compiles.  */
+  if (! profile_arc_flag)
+    return const0_rtx;
+
+  if (!validate_arglist (exp, POINTER_TYPE,
+ 			 POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  /* Argument 0 is the profile update function to call.  */
+  func = CALL_EXPR_ARG (exp, 0);
+  gcc_assert (TREE_CODE (func) == ADDR_EXPR);
+  gcc_assert (TREE_CODE (TREE_OPERAND (func, 0)) == FUNCTION_DECL);
+  fndecl = TREE_OPERAND (func, 0);
+
+  /* Argument 1 is the counter address to pass to the profile
+     update function.  */
+  ctr = CALL_EXPR_ARG (exp, 1);
+
+  /* Argument 1 is the data being profiled to pass to the profile
+     update function.  */
+  data = CALL_EXPR_ARG (exp, 2);
+
+  call = build_call_expr_loc (loc, fndecl, 2, ctr, data);
+
+  return expand_normal (call);
+}
+
+/* Expand a call to __builtin_profile_register_handler.  Passed a pointer to
+   a routine that should be called atexit in -fprofile-generate binaries.  */
+
+static rtx
+expand_builtin_profile_register_handler (tree exp)
+{
+  tree fn, fndecl;
+
+  /* This is only relevent to -fprofile-generate compiles.  */
+  if (! profile_arc_flag)
+    return const0_rtx;
+
+  /* Argument 0 is the function to call atexit.  */
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  tree fntype = build_function_type_list (void_type_node, ptr_type_node,
+                                          NULL_TREE);
+  fndecl = build_fn_decl ("__gcov_register_profile_handler", fntype);
+  fn = build_call_nofold_loc (EXPR_LOCATION (exp), fndecl, 1, arg0);
+  gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+  CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+  return expand_call (fn, const0_rtx, 1);
+}
+
+/* Expand a call to __builtin_profile_record_parameter.  Passed a pointer to
+   the string holding the parameter name and an integer value to associate
+   with it. These are passed to a synthesized call to
+   __gcov_record_parameter_value.  */
+
+static rtx
+expand_builtin_profile_record_parameter (tree exp)
+{
+  tree fn, fndecl;
+
+  /* This is only relevent to -fprofile-generate compiles.  */
+  if (! profile_arc_flag)
+    return const0_rtx;
+
+  /* Argument 0 is the parameter name.  */
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  /* Argument 0 is the value to associate with the parameter name.  */
+  tree arg1 = CALL_EXPR_ARG (exp, 1);
+  tree fntype = build_function_type_list (void_type_node, const_ptr_type_node,
+                                          integer_type_node, NULL_TREE);
+  fndecl = build_fn_decl ("__gcov_record_parameter_value", fntype);
+  fn = build_call_nofold_loc (EXPR_LOCATION (exp), fndecl, 2, arg0, arg1);
+  gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+  CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+  return expand_call (fn, const0_rtx, 1);
+}
+
 /* Get a MEM rtx for expression EXP which is the address of an operand
    to be used in a string instruction (cmpstrsi, movmemsi, ..).  LEN is
    the maximum length of the block of memory that might be accessed or
@@ -6375,6 +6467,12 @@  expand_builtin (tree exp, rtx target, rtx subtarge
       return expand_builtin_expect (exp, target);
     case BUILT_IN_ASSUME_ALIGNED:
       return expand_builtin_assume_aligned (exp, target);
+    case BUILT_IN_PROFILE_INVOKE:
+      return expand_builtin_profile_invoke (exp);
+    case BUILT_IN_PROFILE_REGISTER_HANDLER:
+      return expand_builtin_profile_register_handler (exp);
+    case BUILT_IN_PROFILE_RECORD_PARAMETER:
+      return expand_builtin_profile_record_parameter (exp);
     case BUILT_IN_PREFETCH:
       expand_builtin_prefetch (exp);
       return const0_rtx;
Index: gcc/builtins.def
===================================================================
--- gcc/builtins.def	(revision 210624)
+++ gcc/builtins.def	(working copy)
@@ -793,6 +793,18 @@  DEF_BUILTIN (BUILT_IN_PROFILE_FUNC_ENTER, "__cyg_p
 DEF_BUILTIN (BUILT_IN_PROFILE_FUNC_EXIT, "__cyg_profile_func_exit", BUILT_IN_NORMAL, BT_FN_VOID_PTR_PTR, BT_LAST,
 	     false, false, false, ATTR_NULL, true, true)
 
+DEF_BUILTIN (BUILT_IN_PROFILE_INVOKE, "__builtin_profile_invoke",
+             BUILT_IN_NORMAL, BT_FN_VOID_PTR_FN_VOID_PTR_LONGLONG_PTR_LONGLONG,
+             BT_LAST, false, false, false, ATTR_NULL, true, true)
+
+DEF_BUILTIN (BUILT_IN_PROFILE_REGISTER_HANDLER, "__builtin_profile_register_handler",
+             BUILT_IN_NORMAL, BT_FN_VOID_PTR_FN_VOID,
+             BT_LAST, false, false, false, ATTR_NULL, true, true)
+
+DEF_BUILTIN (BUILT_IN_PROFILE_RECORD_PARAMETER, "__builtin_profile_record_parameter",
+             BUILT_IN_NORMAL, BT_FN_VOID_CONST_PTR_LONGLONG,
+             BT_LAST, false, false, false, ATTR_NULL, true, true)
+
 /* TLS thread pointer related builtins.  */
 DEF_BUILTIN (BUILT_IN_THREAD_POINTER, "__builtin_thread_pointer",
 	     BUILT_IN_NORMAL, BT_FN_PTR, BT_LAST,
Index: gcc/builtin-types.def
===================================================================
--- gcc/builtin-types.def	(revision 210624)
+++ gcc/builtin-types.def	(working copy)
@@ -362,6 +362,18 @@  DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_INT_INT,
 		     BT_VOID, BT_PTR, BT_INT, BT_INT)
 DEF_FUNCTION_TYPE_3 (BT_FN_VOID_CONST_PTR_PTR_SIZE,
 		     BT_VOID, BT_CONST_PTR, BT_PTR, BT_SIZE)
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_PTR_LONGLONG,
+		     BT_VOID, BT_PTR, BT_LONGLONG)
+DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR_LONGLONG, BT_FN_VOID_PTR_LONGLONG)
+DEF_FUNCTION_TYPE_3 (BT_FN_VOID_PTR_FN_VOID_PTR_LONGLONG_PTR_LONGLONG,
+		     BT_VOID, BT_PTR_FN_VOID_PTR_LONGLONG, BT_PTR, BT_LONGLONG)
+
+DEF_POINTER_TYPE (BT_PTR_FN_VOID, BT_FN_VOID)
+DEF_FUNCTION_TYPE_1 (BT_FN_VOID_PTR_FN_VOID, BT_VOID, BT_PTR_FN_VOID)
+
+DEF_FUNCTION_TYPE_2 (BT_FN_VOID_CONST_PTR_LONGLONG, BT_VOID, BT_CONST_PTR,
+                     BT_LONGLONG)
+
 DEF_FUNCTION_TYPE_3 (BT_FN_INT_STRING_CONST_STRING_VALIST_ARG,
 		     BT_INT, BT_STRING, BT_CONST_STRING, BT_VALIST_ARG)
 DEF_FUNCTION_TYPE_3 (BT_FN_INT_CONST_STRING_CONST_STRING_VALIST_ARG,
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	(revision 210624)
+++ gcc/c-family/c-common.c	(working copy)
@@ -316,6 +316,7 @@  static tree handle_noclone_attribute (tree *, tree
 static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
 static tree handle_always_inline_attribute (tree *, tree, tree, int,
 					    bool *);
+static tree handle_profile_init_attribute (tree *, tree, tree, int, bool *);
 static tree handle_gnu_inline_attribute (tree *, tree, tree, int, bool *);
 static tree handle_artificial_attribute (tree *, tree, tree, int, bool *);
 static tree handle_flatten_attribute (tree *, tree, tree, int, bool *);
@@ -629,6 +630,8 @@  const struct attribute_spec c_common_attribute_tab
 			      handle_always_inline_attribute, false },
   { "gnu_inline",             0, 0, true,  false, false,
 			      handle_gnu_inline_attribute, false },
+  { "profile_init",           0, 0, true,  false, false,
+			      handle_profile_init_attribute, false },
   { "artificial",             0, 0, true,  false, false,
 			      handle_artificial_attribute, false },
   { "flatten",                0, 0, true,  false, false,
@@ -6582,6 +6585,40 @@  handle_noinline_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle a "profile_init" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_profile_init_attribute (tree *node, tree name,
+                               tree ARG_UNUSED (args),
+                               int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree decl = *node;
+  tree type = TREE_TYPE (decl);
+
+  if (TREE_CODE (decl) == FUNCTION_DECL
+      && TREE_CODE (type) == FUNCTION_TYPE
+      && decl_function_context (decl) == 0)
+    {
+      /* Profile init function is a static initialization routine
+         in -fprofile-generate compiles, and discarded otherwise.  */
+      if (profile_arc_flag)
+        {
+          DECL_STATIC_CONSTRUCTOR (decl) = 1;
+          SET_DECL_INIT_PRIORITY (decl, DEFAULT_INIT_PRIORITY);
+          TREE_USED (decl) = 1;
+          DECL_PRESERVE_P (decl) = 1;
+        }
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 /* Handle a "noclone" attribute; arguments as in
    struct attribute_spec.handler.  */
 
Index: gcc/c-family/c-opts.c
===================================================================
--- gcc/c-family/c-opts.c	(revision 210624)
+++ gcc/c-family/c-opts.c	(working copy)
@@ -1088,6 +1088,7 @@  c_common_parse_file (void)
     {
       c_finish_options ();
       pch_init ();
+      set_profile_parameters (parse_in);
       set_lipo_c_parsing_context (parse_in, i, verbose);
       push_file_scope ();
 
Index: gcc/common.opt
===================================================================
--- gcc/common.opt	(revision 210624)
+++ gcc/common.opt	(working copy)
@@ -1813,6 +1813,10 @@  fprofile-use=
 Common Joined RejectNegative
 Enable common options for performing profile feedback directed optimizations, and set -fprofile-dir=
 
+fprofile-use-parameters
+Common Var(flag_profile_use_parameters)
+Use parameters section of profile data
+
 fprofile-values
 Common Report Var(flag_profile_values)
 Insert code to profile values of expressions
Index: gcc/coverage.c
===================================================================
--- gcc/coverage.c	(revision 210624)
+++ gcc/coverage.c	(working copy)
@@ -157,6 +157,9 @@  static unsigned num_cpp_includes = 0;
 /* True if the current module has any asm statements.  */
 static bool has_asm_statement;
 
+/* List of parameter values read in from the gcda file.  */
+static struct gcov_parameter_value *gcov_parameter_values = NULL;
+
 /* Forward declarations.  */
 static void read_counts_file (const char *, unsigned);
 static tree build_var (tree, tree, int);
@@ -787,6 +790,8 @@  read_counts_file (const char *da_file_name, unsign
                                   sum.ctrs[GCOV_COUNTER_ARCS].histogram);
 	  new_summary = 0;
 	}
+      else if (tag == GCOV_TAG_PARAMETERS)
+        gcov_parameter_values = gcov_read_parameters (length);
       else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
 	{
 	  counts_entry_t **slot, *entry, elt;
@@ -2764,6 +2769,42 @@  coverage_finish (void)
     }
 }
 
+/* Add the parameters recorded in the GCOV_TAG_PARAMETERS section
+   as preprocessor macro defines.  */
+void
+set_profile_parameters (struct cpp_reader *parse_in)
+{
+  struct gcov_parameter_value *curr_parm;
+
+  if (!flag_profile_use_parameters)
+    return;
+
+  if (!gcov_parameter_values)
+    return;
+
+  /* Since set_profile_parameters is invoked very early, before the pass
+     manager, we need to set up the dumping explicitly. This is
+     similar to the handling in finish_optimization_passes.  */
+  dump_start (pass_profile.pass.static_pass_number, NULL);
+
+  for (curr_parm = gcov_parameter_values; curr_parm;
+       curr_parm = curr_parm->next)
+    {
+      /* Enough for macro name, plus the gcov_type value, equal sign and null
+         termination.  */
+      char *parameter = XNEWVEC (char, strlen (curr_parm->macro_name) + 25);
+      sprintf (parameter, "%s=%ld",
+               curr_parm->macro_name, curr_parm->value);
+      cpp_define (parse_in, parameter);
+      if (dump_enabled_p ())
+        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, input_location,
+                         "Add -D%s from profile\n", parameter);
+      XDELETEVEC (parameter);
+    }
+
+  dump_finish (pass_profile.pass.static_pass_number);
+}
+
 /* Add S to the end of the string-list, the head and tail of which are
    pointed-to by HEAD and TAIL, respectively.  */
 
Index: gcc/gcov.c
===================================================================
--- gcc/gcov.c	(revision 210624)
+++ gcc/gcov.c	(working copy)
@@ -1430,6 +1430,8 @@  read_count_file (function_t *fns)
 	  object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
 	  program_count++;
 	}
+      else if (tag == GCOV_TAG_PARAMETERS)
+        gcov_read_parameters (length);
       else if (tag == GCOV_TAG_FUNCTION && !length)
 	; /* placeholder  */
       else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
Index: gcc/gcov-dump.c
===================================================================
--- gcc/gcov-dump.c	(revision 210624)
+++ gcc/gcov-dump.c	(working copy)
@@ -39,6 +39,7 @@  static void tag_arcs (const char *, unsigned, unsi
 static void tag_lines (const char *, unsigned, unsigned);
 static void tag_counters (const char *, unsigned, unsigned);
 static void tag_summary (const char *, unsigned, unsigned);
+static void tag_parameters (const char *, unsigned, unsigned);
 static void dump_working_sets (const char *filename ATTRIBUTE_UNUSED,
                                const struct gcov_ctr_summary *summary);
 static void tag_module_info (const char *, unsigned, unsigned);
@@ -77,6 +78,7 @@  static const tag_format_t tag_table[] =
   {GCOV_TAG_LINES, "LINES", tag_lines},
   {GCOV_TAG_OBJECT_SUMMARY, "OBJECT_SUMMARY", tag_summary},
   {GCOV_TAG_PROGRAM_SUMMARY, "PROGRAM_SUMMARY", tag_summary},
+  {GCOV_TAG_PARAMETERS, "PARAMETERS", tag_parameters},
   {GCOV_TAG_MODULE_INFO, "MODULE INFO", tag_module_info},
   {0, NULL, NULL}
 };
@@ -624,3 +626,22 @@  dump_working_sets (const char *filename ATTRIBUTE_
                (HOST_WIDEST_INT)ws_info->min_counter);
     }
 }
+
+static void
+tag_parameters (const char *filename ATTRIBUTE_UNUSED,
+                unsigned tag ATTRIBUTE_UNUSED, unsigned length)
+{
+  struct gcov_parameter_value *parameters;
+
+  parameters = gcov_read_parameters (length);
+
+  while (parameters)
+    {
+      printf ("\n");
+      print_prefix (filename, 0, 0);
+      printf ("\t\t%s = ", parameters->macro_name);
+      printf (HOST_WIDEST_INT_PRINT_DEC,
+	      (HOST_WIDEST_INT)parameters->value);
+      parameters = parameters->next;
+    }
+}
Index: gcc/gcov-io.c
===================================================================
--- gcc/gcov-io.c	(revision 210624)
+++ gcc/gcov-io.c	(working copy)
@@ -52,7 +52,8 @@  GCOV_LINKAGE struct gcov_var
   /* 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.  */
+     or 8 byte objects, except for strings, which are handled specially
+     by gcov_write_string.  */
   gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1];
 #else
   int endian;                   /* Swap endianness.  */
@@ -268,6 +269,26 @@  gcov_allocate (unsigned length)
 }
 #endif
 
+/* Return the number of words STRING would need including the length
+   field in the output stream itself.  This should be identical to
+   "alloc" calculation in gcov_write_string().  */
+
+static gcov_unsigned_t
+gcov_string_length (const char *string)
+{
+  gcov_unsigned_t len = (string) ? strlen (string) : 0;
+  /* + 1 because of the length field.  */
+  gcov_unsigned_t alloc = 1 + ((len + 4) >> 2);
+
+#if IN_LIBGCOV
+  /* Currently, libgcov cannot write strings larger than
+     GCOV_BLOCK_SIZE, due to the static buffer size.  */
+  gcc_assert (alloc < GCOV_BLOCK_SIZE);
+#endif
+
+  return alloc;
+}
+
 #if !IN_GCOV
 /* Write out the current block, if needs be.  */
 
@@ -337,7 +358,6 @@  gcov_write_counter (gcov_type value)
 }
 #endif /* IN_LIBGCOV */
 
-#if !IN_LIBGCOV
 /* Write STRING to coverage file.  Sets error flag on file
    error, overflow flag on overflow */
 
@@ -352,15 +372,41 @@  gcov_write_string (const char *string)
     {
       length = strlen (string);
       alloc = (length + 4) >> 2;
+#if IN_LIBGCOV
+      /* Currently, libgcov cannot write strings larger than
+         GCOV_BLOCK_SIZE, due to the static buffer size.  */
+      gcc_assert (alloc + 1 < GCOV_BLOCK_SIZE);
+      /* Ensure we do not overflow buffer, which is statically
+         allocated to GCOV_BLOCK_SIZE + 1 in libgcov. Since
+         gcov_write_words will write out a GCOV_BLOCK_SIZE at
+         a time and expects to only exceed this by at most 1,
+         we need to check whether the offset is large enough to
+         cause a block to be written on entry to gcov_write_words and if
+         not, whether the new string would cause the buffer to
+         overflow. If so, write out the current buffer contents
+         to make space for string and the subsequent call
+         to gcov_write_words (which is otherwise called with
+         1 or 2 words).  */
+      if (gcov_var.offset < GCOV_BLOCK_SIZE
+          && gcov_var.offset + alloc + 1 >= GCOV_BLOCK_SIZE)
+        {
+          gcov_write_block (gcov_var.offset);
+          gcc_assert (!gcov_var.offset);
+        }
+#endif
     }
 
   buffer = gcov_write_words (1 + alloc);
 
   buffer[0] = alloc;
-  buffer[alloc] = 0;
-  memcpy (&buffer[1], string, length);
+
+  /* Copy in string if it is non-empty.  */
+  if (alloc)
+    {
+      buffer[alloc] = 0;
+      memcpy (&buffer[1], string, length);
+    }
 }
-#endif
 
 #if !IN_LIBGCOV
 /* Write a tag TAG and reserve space for the record length. Return a
@@ -465,6 +511,36 @@  gcov_write_summary (gcov_unsigned_t tag, const str
         }
     }
 }
+
+/* Write parameter values to the gcov file. These should be applied
+   to profile-use compiles as macro definitions by the compiler.  */
+
+GCOV_LINKAGE void
+gcov_write_parameters (struct gcov_parameter_value *parameters)
+{
+  gcov_unsigned_t len = 0;
+  struct gcov_parameter_value *curr_parm;
+
+  if (!parameters)
+    return;
+
+  for (curr_parm = parameters; curr_parm;
+       curr_parm = curr_parm->next)
+    {
+      /* Each parameter contains a string followed by a 2-word
+         counter value.  */
+      len += gcov_string_length (curr_parm->macro_name) + 2;
+    }
+
+  gcov_write_tag_length (GCOV_TAG_PARAMETERS, len);
+
+  for (curr_parm = parameters; curr_parm;
+       curr_parm = curr_parm->next)
+    {
+      gcov_write_string (curr_parm->macro_name);
+      gcov_write_counter (curr_parm->value);
+    }
+}
 #endif /* IN_LIBGCOV */
 
 #endif /*!IN_GCOV */
@@ -555,7 +631,6 @@  gcov_read_counter (void)
    buffer, or NULL on empty string. You must copy the string before
    calling another gcov function.  */
 
-#if !IN_LIBGCOV || IN_GCOV_TOOL
 GCOV_LINKAGE const char *
 gcov_read_string (void)
 {
@@ -566,7 +641,6 @@  gcov_read_string (void)
 
   return (const char *) gcov_read_words (length);
 }
-#endif
 
 GCOV_LINKAGE void
 gcov_read_summary (struct gcov_summary *summary)
@@ -685,6 +759,42 @@  gcov_read_module_info (struct gcov_module_info *mo
 }
 #endif
 
+/* Read LENGTH words as parameter values. These should be applied
+   to profile-use compiles as macro definitions by the compiler.
+   Return linked list of parameters.  */
+
+GCOV_LINKAGE struct gcov_parameter_value *
+gcov_read_parameters (gcov_unsigned_t length)
+{
+  unsigned len = length;
+  struct gcov_parameter_value *curr_parm, *parameters = 0;
+  const char *str;
+
+  while (len > 0)
+    {
+      unsigned cur_len;
+      str = gcov_read_string ();
+#if IN_LIBGCOV
+      curr_parm = (struct gcov_parameter_value *)
+          xmalloc (sizeof (struct gcov_parameter_value));
+      curr_parm->macro_name = (char *) xmalloc (strlen (str) + 1);
+      strcpy (curr_parm->macro_name, str);
+#else
+      curr_parm = (struct gcov_parameter_value *)
+          xmalloc (sizeof (struct gcov_parameter_value));
+      curr_parm->macro_name = str ? xstrdup (str) : 0;
+#endif
+      curr_parm->next = parameters;
+      parameters = curr_parm;
+      curr_parm->value = gcov_read_counter ();
+      cur_len = gcov_string_length (str) + 2;
+      gcc_assert (len >= cur_len);
+      len -= cur_len;
+    }
+
+  return parameters;
+}
+
 #if !IN_LIBGCOV || IN_GCOV_TOOL
 /* Reset to a known position.  BASE should have been obtained from
    gcov_position, LENGTH should be a record length.  */
Index: gcc/gcov-io.h
===================================================================
--- gcc/gcov-io.h	(revision 210624)
+++ gcc/gcov-io.h	(working copy)
@@ -129,7 +129,7 @@  see the files COPYING3 and COPYING.RUNTIME respect
    blocks they are for.
 
    The data file contains the following records.
-        data: {unit summary:object summary:program* function-data*}*
+        data: {unit summary:program* parameter-data function-data*}*
 	unit: header int32:checksum
         function-data:	announce_function present counts
 	announce_function: header int32:ident
@@ -141,6 +141,8 @@  see the files COPYING3 and COPYING.RUNTIME respect
 			int64:max int64:sum_max histogram
         histogram: {int32:bitvector}8 histogram-buckets*
         histogram-buckets: int32:num int64:min int64:sum
+        parameter-data: header parm-value*
+        parm-value: string:macro_name int64:value
 
    The ANNOUNCE_FUNCTION record is the same as that in the note file,
    but without the source location.  The COUNTS gives the
@@ -148,9 +150,8 @@  see the files COPYING3 and COPYING.RUNTIME respect
    program.  The checksum is used for whole program summaries, and
    disambiguates different programs which include the same
    instrumented object file.  There may be several program summaries,
-   each with a unique checksum.  The object summary's checksum is
-   zero.  Note that the data file might contain information from
-   several runs concatenated, or the data might be merged.
+   each with a unique checksum.  Note that the data file might contain
+   information from several runs concatenated, or the data might be merged.
 
    This file is included by both the compiler, gcov tools and the
    runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
@@ -257,6 +258,7 @@  typedef unsigned HOST_WIDEST_INT gcov_type_unsigne
 #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000)
 #define GCOV_TAG_SUMMARY_LENGTH(NUM)  \
         (1 + GCOV_COUNTERS_SUMMABLE * (10 + 3 * 2) + (NUM) * 5)
+#define GCOV_TAG_PARAMETERS    ((gcov_unsigned_t)0xa5000000)
 #define GCOV_TAG_MODULE_INFO ((gcov_unsigned_t)0xab000000)
 #define GCOV_TAG_AFDO_FILE_NAMES ((gcov_unsigned_t)0xaa000000)
 #define GCOV_TAG_AFDO_FUNCTION ((gcov_unsigned_t)0xac000000)
@@ -436,6 +438,15 @@  extern unsigned primary_module_id;
    && !((module_infos[0]->lang & GCOV_MODULE_ASM_STMTS)			\
 	&& flag_ripa_disallow_asm_modules))
 
+/* Parameter values determined at profile time that should be applied to
+   profile-use compiles as macro definitions.  */
+struct gcov_parameter_value
+{
+  struct gcov_parameter_value *next;
+  char *macro_name;
+  gcov_type value;
+};
+
 #if !defined(inhibit_libc)
 
 /* Functions for reading and writing gcov files. In libgcov you can
@@ -457,6 +468,8 @@  GCOV_LINKAGE int gcov_close (void) ATTRIBUTE_HIDDE
 GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE struct gcov_parameter_value *gcov_read_parameters (gcov_unsigned_t)
+  ATTRIBUTE_HIDDEN;
 GCOV_LINKAGE const char *gcov_read_string (void);
 GCOV_LINKAGE void gcov_sync (gcov_position_t /*base*/,
 			     gcov_unsigned_t /*length */);
@@ -469,12 +482,12 @@  GCOV_LINKAGE void gcov_read_module_info (struct gc
 #if !IN_GCOV
 /* Available outside gcov */
 GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t) ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_string (const char *);
 #endif
 
 #if !IN_GCOV && !IN_LIBGCOV
 /* Available only in compiler */
 GCOV_LINKAGE unsigned gcov_histo_index (gcov_type value);
-GCOV_LINKAGE void gcov_write_string (const char *);
 GCOV_LINKAGE gcov_position_t gcov_write_tag (gcov_unsigned_t);
 GCOV_LINKAGE void gcov_write_length (gcov_position_t /*position*/);
 #endif
Index: gcc/opts.h
===================================================================
--- gcc/opts.h	(revision 210624)
+++ gcc/opts.h	(working copy)
@@ -378,6 +378,7 @@  extern void control_warning_option (unsigned int o
 				    struct gcc_options *opts_set,
 				    diagnostic_context *dc);
 extern void print_ignored_options (void);
+extern void set_profile_parameters (struct cpp_reader *parse_in);
 extern void add_input_filename (const char *filename);
 extern void add_module_info (unsigned mod_id, bool is_primary, int index);
 extern void set_lipo_c_parsing_context (struct cpp_reader *parse_in, int i, bool verbose);
Index: gcc/testsuite/g++.dg/tree-prof/fdlo1.C
===================================================================
--- gcc/testsuite/g++.dg/tree-prof/fdlo1.C	(revision 0)
+++ gcc/testsuite/g++.dg/tree-prof/fdlo1.C	(revision 0)
@@ -0,0 +1,82 @@ 
+/* Ensure that parameter profiling behaves as expected.  */
+/* { dg-options "-fprofile-use-parameters -fdump-tree-profile_estimate-all" } */
+
+#include<stdio.h>
+
+class Container
+{
+public:
+  Container();
+  void resize (size_t s);
+
+private:
+
+/* Profiling specific section:  */
+
+  typedef struct ProfCounter {
+    long long total;
+    int count;
+  } ProfCounter;
+
+  static void profile_init (void) __attribute__((profile_init));
+  static void profile_handler (void);
+  static void profile_update (void *, long long value);
+  static ProfCounter *counter_;
+};
+
+Container::ProfCounter *Container::counter_;
+
+Container::Container()
+{
+#ifndef BUF_SIZE
+#define BUF_SIZE 32
+#endif
+  fprintf (stderr, "Container BUF_SIZE is %d\n", BUF_SIZE);
+}
+
+void
+Container::profile_init (void)
+{
+  /* Counter creation */
+  counter_ = new ProfCounter ();
+  counter_->total = 0;
+  counter_->count = 0;
+
+  /* Register handler:  */
+  __builtin_profile_register_handler (&profile_handler);
+}
+
+void
+Container::profile_handler (void)
+{
+  /* Process counter_ data */
+  long long optimal_buffer_size = counter_->total/counter_->count;
+
+  /* Now record the optimal buffer size */
+  __builtin_profile_record_parameter ("BUF_SIZE", optimal_buffer_size);
+}
+
+void
+Container::resize (size_t s)
+{
+  __builtin_profile_invoke (profile_update, counter_, s);
+}
+
+void
+Container::profile_update (void * profile_counter, long long data)
+{
+  ProfCounter *p = (ProfCounter *) profile_counter;
+  p->total += data;
+  p->count++;
+}
+
+int main()
+{
+  Container s;
+  s.resize(10);
+  s.resize(20);
+  return 0;
+}
+
+/* { dg-final-use { scan-tree-dump "note: Add -DBUF_SIZE=15 from profile" "profile_estimate"} } */
+/* { dg-final-use { cleanup-tree-dump "profile_estimate" } } */
Index: libgcc/libgcov-driver.c
===================================================================
--- libgcc/libgcov-driver.c	(revision 210624)
+++ libgcc/libgcov-driver.c	(working copy)
@@ -128,6 +128,19 @@  struct gcov_summary_buffer
   struct gcov_summary summary;
 };
 
+struct gcov_handler_list
+{
+  struct gcov_handler_list *next;
+  void (*handler) (void);
+};
+
+/* List of user-specified handlers to invoke atexit.  */
+static struct gcov_handler_list *gcov_registered_handlers = NULL;
+
+/* List of paramter values recorded by __gcov_record_parameter_value
+   invocations that should be emitted to parameter section of profile.  */
+static struct gcov_parameter_value *gcov_parameter_values = NULL;
+
 /* Chain of per-object gcov structures.  */
 static struct gcov_info *__gcov_list;
 
@@ -277,6 +290,66 @@  gcov_compute_histogram (struct gcov_summary *sum)
     }
 }
 
+/* Find and return parameter in LIST with name matching MACRO_NAME,
+   or NULL if no match found.  */
+
+static struct gcov_parameter_value *
+find_parameter (struct gcov_parameter_value *list, const char *macro_name)
+{
+  struct gcov_parameter_value *cur_parm = list;
+
+  while (cur_parm)
+    {
+      if (!strcmp(cur_parm->macro_name, macro_name))
+        return cur_parm;
+      cur_parm = cur_parm->next;
+    }
+
+  return NULL;
+}
+
+/* Merge parameters from NEW_PARMS list into SAVED_PARMS.  Any parameters
+   with matching names will have their values merged.  Returns merged list.  */
+
+static struct gcov_parameter_value *
+merge_parameters (struct gcov_parameter_value *saved_parms,
+                  struct gcov_parameter_value *new_parms)
+
+{
+  struct gcov_parameter_value *cur_new_parm, *cur_merge_parm, *merged_parms;
+
+  merged_parms = saved_parms;
+
+  for (cur_new_parm = new_parms; cur_new_parm;
+       cur_new_parm = cur_new_parm->next)
+    {
+      cur_merge_parm = find_parameter (saved_parms, cur_new_parm->macro_name);
+
+      if (cur_merge_parm)
+        {
+          /* Simply average them for now. The best merge strategy will
+             depend on how they were computed in the first place.
+             In the future this can be handled by recording a parameter
+             type along with the parameter, that tells how to merge.  */
+          cur_merge_parm->value
+              = (cur_merge_parm->value + cur_new_parm->value) / 2;
+        }
+      else
+        {
+          cur_merge_parm = (struct gcov_parameter_value *)
+              xmalloc (sizeof (struct gcov_parameter_value));
+          cur_merge_parm->macro_name = (char *)
+              xmalloc (strlen(cur_new_parm->macro_name) + 1);
+          strcpy (cur_merge_parm->macro_name, cur_new_parm->macro_name);
+          cur_merge_parm->value = cur_new_parm->value;
+
+          cur_merge_parm->next = merged_parms;
+          merged_parms = cur_merge_parm;
+        }
+    }
+  return merged_parms;
+}
+
 /* This funtions computes the program level summary and the histo-gram.
    It computes and returns CRC32 and stored summary in THIS_PRG.  */
 
@@ -362,7 +435,8 @@  gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
                       struct gcov_summary *this_prg,
                       gcov_position_t *summary_pos_p,
                       gcov_position_t *eof_pos_p,
-                      gcov_unsigned_t crc32)
+                      gcov_unsigned_t crc32,
+                      struct gcov_parameter_value **merged_parameters)
 {
   gcov_unsigned_t tag, length, version, stamp;
   unsigned t_ix, f_ix;
@@ -418,6 +492,15 @@  gcov_exit_merge_gcda (struct gcov_info *gi_ptr,
     next_summary:;
     }
 
+  if (tag == GCOV_TAG_PARAMETERS)
+    {
+      length = gcov_read_unsigned ();
+      *merged_parameters = gcov_read_parameters (length);
+      *merged_parameters = merge_parameters (*merged_parameters,
+                                             gcov_parameter_values);
+      tag = gcov_read_unsigned ();
+    }
+
   /* Merge execution counts for each function.  */
   for (f_ix = 0; f_ix != gi_ptr->n_functions;
        f_ix++, tag = gcov_read_unsigned ())
@@ -488,7 +571,8 @@  static void
 gcov_exit_write_gcda (struct gcov_info *gi_ptr,
                       const struct gcov_summary *prg_p,
                       const gcov_position_t eof_pos,
-                      const gcov_position_t summary_pos)
+                      const gcov_position_t summary_pos,
+                      struct gcov_parameter_value *merged_parameters)
 
 {
   const struct gcov_ctr_info *ci_ptr;
@@ -523,6 +607,18 @@  gcov_exit_write_gcda (struct gcov_info *gi_ptr,
       sum_buffer = next_sum_buffer;
     }
 
+  if (merged_parameters)
+    {
+      gcov_write_parameters (merged_parameters);
+
+      while (merged_parameters)
+        {
+          struct gcov_parameter_value *cur_parameter = merged_parameters;
+          merged_parameters = merged_parameters->next;
+          free (cur_parameter);
+        }
+    }
+
   /* Write execution counts for each function.  */
   for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
     {
@@ -702,6 +798,7 @@  gcov_exit_dump_gcov (struct gcov_info *gi_ptr, str
                      struct gcov_summary *this_prg)
 {
   struct gcov_summary prg; /* summary for this object over all program.  */
+  struct gcov_parameter_value *merged_parameters = NULL;
   int error;
   gcov_unsigned_t tag;
   gcov_position_t summary_pos = 0;
@@ -723,8 +820,8 @@  gcov_exit_dump_gcov (struct gcov_info *gi_ptr, str
           gcov_error ("profiling:%s:Not a gcov data file\n", gi_filename);
           goto read_fatal;
         }
-      error = gcov_exit_merge_gcda (gi_ptr, &prg, this_prg, &summary_pos, &eof_pos,
-				    crc32);
+      error = gcov_exit_merge_gcda (gi_ptr, &prg, this_prg, &summary_pos,
+                                    &eof_pos, crc32, &merged_parameters);
       if (error == -1)
         goto read_fatal;
     }
@@ -741,7 +838,10 @@  gcov_exit_dump_gcov (struct gcov_info *gi_ptr, str
   if (error == -1)
     goto read_fatal;
 
-  gcov_exit_write_gcda (gi_ptr, &prg, eof_pos, summary_pos);
+  if (!merged_parameters)
+      merged_parameters = merge_parameters (NULL, gcov_parameter_values);
+
+  gcov_exit_write_gcda (gi_ptr, &prg, eof_pos, summary_pos, merged_parameters);
   /* fall through */
 
 read_fatal:;
@@ -851,6 +951,12 @@  gcov_exit (void)
   if (gcov_dump_complete)
     return;
 
+  struct gcov_handler_list *curr_handler;
+  for (curr_handler = gcov_registered_handlers;
+       curr_handler != NULL;
+       curr_handler = curr_handler->next)
+    curr_handler->handler ();
+
   crc32 = gcov_exit_compute_summary (&this_prg);
 
   allocate_filename_struct (&gf);
@@ -868,6 +974,14 @@  gcov_exit (void)
         dump_module_info |= gi_ptr->mod_info->is_primary;
     }
 
+  struct gcov_parameter_value *cur_parameter = 0;
+  while (gcov_parameter_values)
+    {
+      cur_parameter = gcov_parameter_values;
+      gcov_parameter_values = gcov_parameter_values->next;
+      free (cur_parameter);
+    }
+
   if (dump_module_info)
     gcov_dump_module_info (&gf);
 
@@ -1008,5 +1122,39 @@  gcov_gcda_file_size (const struct gcov_info *gi_pt
   return size*4;
 }
 
+/* Register a handler routine to execute atexit.  */
+
+void
+__gcov_register_profile_handler (void (*handler) (void))
+{
+  struct gcov_handler_list *new_handler
+      = (struct gcov_handler_list *) xmalloc (sizeof (struct gcov_handler_list));
+
+  new_handler->handler = handler;
+  new_handler->next = gcov_registered_handlers;
+  gcov_registered_handlers = new_handler;
+}
+
+/* Record a parameter MACRO_NAME and VALUE to save in the profile to apply
+   via -DMACRO=VALUE on profile use compiles.  */
+
+void
+__gcov_record_parameter_value (const char *macro_name, gcov_type value)
+{
+  struct gcov_parameter_value *new_parm;
+
+  if (find_parameter (gcov_parameter_values, macro_name))
+    return;
+
+  new_parm
+      = (struct gcov_parameter_value *) xmalloc (sizeof (struct gcov_parameter_value));
+  new_parm->macro_name = (char *) xmalloc (strlen(macro_name) + 1);
+  strcpy (new_parm->macro_name, macro_name);
+  new_parm->value = value;
+
+  new_parm->next = gcov_parameter_values;
+  gcov_parameter_values = new_parm;
+}
+
 #endif /* L_gcov */
 #endif /* inhibit_libc */
Index: libgcc/libgcov.h
===================================================================
--- libgcc/libgcov.h	(revision 210624)
+++ libgcc/libgcov.h	(working copy)
@@ -171,12 +171,14 @@  typedef unsigned gcov_type_unsigned __attribute__
 #define gcov_read_unsigned __gcov_read_unsigned
 #define gcov_read_counter __gcov_read_counter
 #define gcov_read_summary __gcov_read_summary
+#define gcov_read_parameters __gcov_read_parameters
+#define gcov_read_string __gcov_read_string
 #define gcov_read_module_info __gcov_read_module_info
 #define gcov_sort_n_vals __gcov_sort_n_vals
 
 /* Poison these, so they don't accidentally slip in.  */
-#pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length
-#pragma GCC poison /*gcov_read_string gcov_sync*/ gcov_time gcov_magic
+#pragma GCC poison gcov_write_tag gcov_write_length
+#pragma GCC poison /*gcov_sync*/ gcov_time gcov_magic
 
 #ifdef HAVE_GAS_HIDDEN
 #define ATTRIBUTE_HIDDEN  __attribute__ ((__visibility__ ("hidden")))
@@ -250,6 +252,13 @@  extern void __gcov_init (struct gcov_info *) ATTRI
 /* Set sampling rate to RATE.  */
 extern void __gcov_set_sampling_rate (unsigned int rate);
 
+/* Register a handler to be called atexit.  */
+extern void __gcov_register_profile_handler (void (*) (void)) ATTRIBUTE_HIDDEN;
+
+/* Record a parameter and associated value in profile.  */
+extern void __gcov_record_parameter_value (const char *, gcov_type)
+  ATTRIBUTE_HIDDEN;
+
 /* Called before fork, to avoid double counting.  */
 extern void __gcov_flush (void) ATTRIBUTE_HIDDEN;
 
@@ -310,6 +319,8 @@  GCOV_LINKAGE void gcov_write_tag_length (gcov_unsi
 GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t /*tag*/,
 				      const struct gcov_summary *)
     ATTRIBUTE_HIDDEN;
+GCOV_LINKAGE void gcov_write_parameters (struct gcov_parameter_value *)
+  ATTRIBUTE_HIDDEN;
 
 GCOV_LINKAGE void gcov_write_module_infos (struct gcov_info *mod_info)
     ATTRIBUTE_HIDDEN;