+Thu Jul 30 13:08:07 1998 Ken Raeburn <raeburn@cygnus.com>
+
+ Function entry/exit profiling instrumentation:
+ * expr.h (profile_function_entry_libfunc,
+ profile_function_exit_libfunc): Declare new variables.
+ * optabs.c: Define them here.
+ (init_optabs): Initialize them.
+ * tree.h (struct tree_decl): New flag
+ no_instrument_function_entry_exit.
+ (DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT): New accessor macro.
+ * c-decl.c (duplicate_decls): Merge it.
+ * c-common.c (enum attrs): New value A_NO_INSTRUMENT_FUNCTION.
+ (init_attributes): Use it for "no_instrument_function".
+ (decl_attributes): Handle it, for functions that have not yet been
+ compiled. Set decl flag.
+ * flags.h (flag_instrument_function_entry_exit): Declare new
+ variable.
+ * toplev.c (flag_instrument_function_entry_exit): Define it here.
+ (f_options): New option "instrument-functions".
+ * function.h (struct function): New field instrument_entry_exit.
+ * function.c (current_function_instrument_entry_exit): New
+ variable.
+ (push_function_context_to, pop_function_context_from): Save and
+ restore.
+ (expand_function_start): Set current_ variable, maybe emit return
+ label and entry profile call.
+ (expand_function_end): Maybe emit exit profile call.
+
Thu Jul 30 00:58:34 1998 Jeffrey A Law (law@cygnus.com)
* i386.md (movqi): When optimizing a load of (const_int 1) into a
int skip_evaluation;
enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
+ A_NO_INSTRUMENT_FUNCTION,
A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS};
add_attribute (A_FORMAT_ARG, "format_arg", 1, 1, 1);
add_attribute (A_WEAK, "weak", 0, 0, 1);
add_attribute (A_ALIAS, "alias", 1, 1, 1);
+ add_attribute (A_NO_INSTRUMENT_FUNCTION, "no_instrument_function", 0, 0, 1);
}
\f
/* Process the attributes listed in ATTRIBUTES and PREFIX_ATTRIBUTES
else
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
break;
+
+ case A_NO_INSTRUMENT_FUNCTION:
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ {
+ error_with_decl (decl,
+ "`%s' attribute applies only to functions",
+ IDENTIFIER_POINTER (name));
+ }
+ else if (DECL_INITIAL (decl))
+ {
+ error_with_decl (decl,
+ "can't set `%s' attribute after definition",
+ IDENTIFIER_POINTER (name));
+ }
+ else
+ DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
+ break;
}
}
}
{
DECL_STATIC_CONSTRUCTOR(newdecl) |= DECL_STATIC_CONSTRUCTOR(olddecl);
DECL_STATIC_DESTRUCTOR (newdecl) |= DECL_STATIC_DESTRUCTOR (olddecl);
+
+ DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
+ |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl);
}
pop_obstacks ();
extern rtx chkr_copy_bitmap_libfunc;
extern rtx chkr_check_exec_libfunc;
extern rtx chkr_check_str_libfunc;
+
+/* For instrument-functions. */
+extern rtx profile_function_entry_libfunc;
+extern rtx profile_function_exit_libfunc;
\f
typedef rtx (*rtxfun) PROTO ((rtx));
The keyword @code{__attribute__} allows you to specify special
attributes when making a declaration. This keyword is followed by an
-attribute specification inside double parentheses. Eight attributes,
-@code{noreturn}, @code{const}, @code{format}, @code{section},
+attribute specification inside double parentheses. Nine attributes,
+@code{noreturn}, @code{const}, @code{format},
+@code{no_instrument_function}, @code{section},
@code{constructor}, @code{destructor}, @code{unused} and @code{weak} are
currently defined for functions. Other attributes, including
@code{section} are supported for variables declarations (@pxref{Variable
treats @code{gettext}, @code{dgettext}, and @code{dcgettext} in this
manner.
+@item no_instrument_function
+@cindex @code{no_instrument_function} function attribute
+If @samp{-finstrument-functions} is given, profiling function calls will
+be generated at entry and exit of most user-compiled functions.
+Functions with this attribute will not be so instrumented.
+
@item section ("section-name")
@cindex @code{section} function attribute
Normally, the compiler places the code it generates in the @code{text} section.
/* Do the full regmove optimization pass. */
extern int flag_regmove;
+
+/* Instrument functions with calls at entry and exit, for profiling. */
+extern int flag_instrument_function_entry_exit;
\f
/* Other basic status info about current function. */
/* Language-specific reason why the current function cannot be made inline. */
char *current_function_cannot_inline;
+/* Nonzero if instrumentation calls for function entry and exit should be
+ generated. */
+int current_function_instrument_entry_exit;
+
/* The FUNCTION_DECL for an inline function currently being expanded. */
tree inline_function_decl;
p->fixup_var_refs_queue = 0;
p->epilogue_delay_list = current_function_epilogue_delay_list;
p->args_info = current_function_args_info;
+ p->instrument_entry_exit = current_function_instrument_entry_exit;
save_tree_status (p, context);
save_storage_status (p);
current_function_epilogue_delay_list = p->epilogue_delay_list;
reg_renumber = 0;
current_function_args_info = p->args_info;
+ current_function_instrument_entry_exit = p->instrument_entry_exit;
restore_tree_status (p, context);
restore_storage_status (p);
valid operands of arithmetic insns. */
init_recog_no_volatile ();
+ current_function_instrument_entry_exit
+ = (flag_instrument_function_entry_exit
+ && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr));
+
/* If function gets a static chain arg, store it in the stack frame.
Do this first, so it gets the first stack slot offset. */
if (current_function_needs_context)
or if it returns a structure, or if it has parm cleanups. */
#ifdef HAVE_return
if (cleanup_label == 0 && HAVE_return
+ && ! current_function_instrument_entry_exit
&& ! current_function_returns_pcc_struct
&& ! (current_function_returns_struct && ! optimize))
return_label = 0;
else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
/* If return mode is void, this decl rtl should not be used. */
DECL_RTL (DECL_RESULT (subr)) = 0;
- else if (parms_have_cleanups)
+ else if (parms_have_cleanups || current_function_instrument_entry_exit)
{
/* If function will end with cleanup code for parms,
compute the return values into a pseudo reg,
}
}
+ if (current_function_instrument_entry_exit)
+ {
+ rtx fun = DECL_RTL (current_function_decl);
+ if (GET_CODE (fun) == MEM)
+ fun = XEXP (fun, 0);
+ else
+ abort ();
+ emit_library_call (profile_function_entry_libfunc, 0, VOIDmode, 2,
+ fun, Pmode,
+ expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
+ 0,
+ hard_frame_pointer_rtx),
+ Pmode);
+ }
+
/* After the display initializations is where the tail-recursion label
should go, if we end up needing one. Ensure we have a NOTE here
since some things (like trampolines) get placed before this. */
}
}
+ if (current_function_instrument_entry_exit)
+ {
+ rtx fun = DECL_RTL (current_function_decl);
+ if (GET_CODE (fun) == MEM)
+ fun = XEXP (fun, 0);
+ else
+ abort ();
+ emit_library_call (profile_function_exit_libfunc, 0, VOIDmode, 2,
+ fun, Pmode,
+ expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
+ 0,
+ hard_frame_pointer_rtx),
+ Pmode);
+ }
+
/* If we had calls to alloca, and this machine needs
an accurate stack pointer to exit the function,
insert some code to save and restore the stack pointer. */
int temp_slot_level;
int target_temp_slot_level;
int var_temp_slot_level;
+ int instrument_entry_exit;
/* This slot is initialized as 0 and is added to
during the nested function. */
struct var_refs_queue *fixup_var_refs_queue;
@end example
This option is designed to be used with @samp{-fcheck-memory-usage}.
+@item -finstrument-functions
+Generate instrumentation calls for entry and exit to functions. Just
+after function entry and just before function exit, the following
+profiling functions will be called with the address of the current
+function and its call site. (On some platforms,
+@code{__builtin_return_address} does not work beyond the current
+function, so the call site information may not be available to the
+profiling functions otherwise.)
+
+@example
+void __cyg_profile_func_enter (void *this_fn, void *call_site);
+void __cyg_profile_func_exit (void *this_fn, void *call_site);
+@end example
+
+The first argument is the address of the start of the current function,
+which may be looked up exactly in the symbol table.
+
+This instrumentation is also done for functions expanded inline in other
+functions. The profiling calls will indicate where, conceptually, the
+inline function is entered and exited. This means that addressable
+versions of such functions must be available. If all your uses of a
+function are expanded inline, this may mean an additional expansion of
+code size. If you use @samp{extern inline} in your C code, an
+addressable version of such functions must be provided. (This is
+normally the case anyways, but if you get lucky and the optimizer always
+expands the functions inline, you might have gotten away without
+providing static copies.)
+
+A function may be given the attribute @code{no_instrument_function}, in
+which case this instrumentation will not be done. This can be used, for
+example, for the profiling functions listed above, high-priority
+interrupt routines, and any functions from which the profiling functions
+cannot safely be called (perhaps signal handlers, if the profiling
+routines generate output or allocate memory).
+
@item -fstack-check
Generate code to verify that you do not go beyond the boundary of the
stack. You should specify this flag if you are running in an
rtx chkr_check_exec_libfunc;
rtx chkr_check_str_libfunc;
+rtx profile_function_entry_libfunc;
+rtx profile_function_exit_libfunc;
+
/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...)
gives the gen_function to make a branch to test that condition. */
chkr_check_exec_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_exec");
chkr_check_str_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_str");
+ /* For function entry/exit instrumentation. */
+ profile_function_entry_libfunc
+ = gen_rtx_SYMBOL_REF (VOIDmode, "__cyg_profile_func_enter");
+ profile_function_exit_libfunc
+ = gen_rtx_SYMBOL_REF (VOIDmode, "__cyg_profile_func_exit");
+
#ifdef HAVE_conditional_trap
init_traps ();
#endif
+1998-07-30 Ken Raeburn <raeburn@cygnus.com>
+
+ * gcc.c-torture/special/eeprof-1.c: New test, for
+ -finstrument-functions.
+ * gcc.c-torture/special/special.exp: Run it.
+
Wed Jul 29 00:17:18 1998 Jeffrey A Law (law@cygnus.com)
* gcc.c-torture/compile/980729-1.c: New test.
--- /dev/null
+#define ASSERT(X) if (!(X)) abort ();
+#define NOCHK __attribute__ ((no_instrument_function))
+
+int entry_calls, exit_calls;
+void (*last_fn_entered)();
+void (*last_fn_exited)();
+
+int main () NOCHK;
+
+void foo ()
+{
+ ASSERT (last_fn_entered == foo);
+}
+
+static void foo2 ()
+{
+ ASSERT (entry_calls == 1 && exit_calls == 0);
+ ASSERT (last_fn_entered == foo2);
+ foo ();
+ ASSERT (entry_calls == 2 && exit_calls == 1);
+ ASSERT (last_fn_entered == foo);
+ ASSERT (last_fn_exited == foo);
+}
+
+void nfoo (void) NOCHK;
+void nfoo ()
+{
+ ASSERT (entry_calls == 2 && exit_calls == 2);
+ ASSERT (last_fn_entered == foo);
+ ASSERT (last_fn_exited == foo2);
+ foo ();
+ ASSERT (entry_calls == 3 && exit_calls == 3);
+ ASSERT (last_fn_entered == foo);
+ ASSERT (last_fn_exited == foo);
+}
+
+int main ()
+{
+ ASSERT (entry_calls == 0 && exit_calls == 0);
+
+ foo2 ();
+
+ ASSERT (entry_calls == 2 && exit_calls == 2);
+ ASSERT (last_fn_entered == foo);
+ ASSERT (last_fn_exited == foo2);
+
+ nfoo ();
+
+ ASSERT (entry_calls == 3 && exit_calls == 3);
+ ASSERT (last_fn_entered == foo);
+
+ return 0;
+}
+
+void __cyg_profile_func_enter (void (*fn)(), void (*parent)()) NOCHK;
+void __cyg_profile_func_exit (void (*fn)(), void (*parent)()) NOCHK;
+
+void __cyg_profile_func_enter (void (*fn)(), void (*parent)())
+{
+ entry_calls++;
+ last_fn_entered = fn;
+}
+void __cyg_profile_func_exit (void (*fn)(), void (*parent)())
+{
+ exit_calls++;
+ last_fn_exited = fn;
+}
# 960224-2
#c-torture 960224-2.c "-E -ansi -pedantic-errors"
+
+c-torture-execute $srcdir/$subdir/eeprof-1.c "-finstrument-functions"
extern int flag_dump_unnumbered;
+/* Instrument functions with calls at entry and exit, for profiling. */
+int flag_instrument_function_entry_exit = 0;
+
/* Table of supported debugging formats. */
static struct
"Generate code to check every memory access" },
{"prefix-function-name", &flag_prefix_function_name, 1,
"Add a prefix to all function names" },
- {"dump-unnumbered", &flag_dump_unnumbered, 1}
+ {"dump-unnumbered", &flag_dump_unnumbered, 1},
+ {"instrument-functions", &flag_instrument_function_entry_exit, 1,
+ "Instrument function entry/exit with profiling calls"},
};
#define NUM_ELEM(a) (sizeof (a) / sizeof ((a)[0]))
multiple translation units should be merged. */
#define DECL_ONE_ONLY(NODE) (DECL_CHECK (NODE)->decl.transparent_union)
+/* Used in FUNCTION_DECLs to indicate that function entry and exit should
+ be instrumented with calls to support routines. */
+#define DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(NODE) ((NODE)->decl.no_instrument_function_entry_exit)
+
/* Additional flags for language-specific uses. */
#define DECL_LANG_FLAG_0(NODE) (DECL_CHECK (NODE)->decl.lang_flag_0)
#define DECL_LANG_FLAG_1(NODE) (DECL_CHECK (NODE)->decl.lang_flag_1)
unsigned static_dtor_flag : 1;
unsigned artificial_flag : 1;
unsigned weak_flag : 1;
- /* room for no more */
unsigned lang_flag_0 : 1;
unsigned lang_flag_1 : 1;
unsigned lang_flag_7 : 1;
unsigned non_addr_const_p : 1;
+ unsigned no_instrument_function_entry_exit : 1;
/* For a FUNCTION_DECL, if inline, this is the size of frame needed.
If built-in, this is the code for which built-in function.