From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1285) id 1726E38387E1; Mon, 13 Jun 2022 11:35:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1726E38387E1 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Eric Botcazou To: gcc-cvs@gcc.gnu.org Subject: [gcc r13-1065] Introduce -finstrument-functions-once X-Act-Checkin: gcc X-Git-Author: Eric Botcazou X-Git-Refname: refs/heads/master X-Git-Oldrev: cb1ecf3819f19a4fc35468010b66b5c1a7b21ee8 X-Git-Newrev: 3b598848f6fdbf7e76eaf2beec1ce94fc3a14b9b Message-Id: <20220613113546.1726E38387E1@sourceware.org> Date: Mon, 13 Jun 2022 11:35:46 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 13 Jun 2022 11:35:46 -0000 https://gcc.gnu.org/g:3b598848f6fdbf7e76eaf2beec1ce94fc3a14b9b commit r13-1065-g3b598848f6fdbf7e76eaf2beec1ce94fc3a14b9b Author: Eric Botcazou Date: Mon Jun 13 13:32:53 2022 +0200 Introduce -finstrument-functions-once The goal is to make it possible to use it in (large) production binaries to do function-level coverage, so the overhead must be minimum and, in particular, there is no protection against data races so the "once" moniker is imprecise. gcc/ * common.opt (finstrument-functions): Set explicit value. (-finstrument-functions-once): New option. * doc/invoke.texi (Program Instrumentation Options): Document it. * gimplify.cc (build_instrumentation_call): New static function. (gimplify_function_tree): Call it to emit the instrumentation calls if -finstrument-functions[-once] is specified. gcc/testsuite/ * gcc.dg/instrument-4.c: New test. Diff: --- gcc/common.opt | 6 +- gcc/doc/invoke.texi | 18 ++++- gcc/gimplify.cc | 137 +++++++++++++++++++++++++++--------- gcc/testsuite/gcc.dg/instrument-4.c | 7 ++ 4 files changed, 133 insertions(+), 35 deletions(-) diff --git a/gcc/common.opt b/gcc/common.opt index 7ca0cceed82..8e961f16b0e 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1890,9 +1890,13 @@ EnumValue Enum(cf_protection_level) String(none) Value(CF_NONE) finstrument-functions -Common Var(flag_instrument_function_entry_exit) +Common Var(flag_instrument_function_entry_exit,1) Instrument function entry and exit with profiling calls. +finstrument-functions-once +Common Var(flag_instrument_function_entry_exit,2) +Instrument function entry and exit with profiling calls invoked once. + finstrument-functions-exclude-function-list= Common RejectNegative Joined -finstrument-functions-exclude-function-list=name,... Do not instrument listed functions. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 174bc09e5cf..b6c0305f198 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -618,7 +618,7 @@ Objective-C and Objective-C++ Dialects}. -fno-stack-limit -fsplit-stack @gol -fvtable-verify=@r{[}std@r{|}preinit@r{|}none@r{]} @gol -fvtv-counts -fvtv-debug @gol --finstrument-functions @gol +-finstrument-functions -finstrument-functions-once @gol -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{}} @gol -fprofile-prefix-map=@var{old}=@var{new} @@ -16395,6 +16395,22 @@ cannot safely be called (perhaps signal handlers, if the profiling routines generate output or allocate memory). @xref{Common Function Attributes}. +@item -finstrument-functions-once +@opindex -finstrument-functions-once +This is similar to @option{-finstrument-functions}, but the profiling +functions are called only once per instrumented function, i.e. the first +profiling function is called after the first entry into the instrumented +function and the second profiling function is called before the exit +corresponding to this first entry. + +The definition of @code{once} for the purpose of this option is a little +vague because the implementation is not protected against data races. +As a result, the implementation only guarantees that the profiling +functions are called at @emph{least} once per process and at @emph{most} +once per thread, but the calls are always paired, that is to say, if a +thread calls the first function, then it will call the second function, +unless it never reaches the exit of the instrumented function. + @item -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{} @opindex finstrument-functions-exclude-file-list diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index cd1796643d7..04990ad91a6 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -16586,6 +16586,51 @@ flag_instrument_functions_exclude_p (tree fndecl) return false; } +/* Build a call to the instrumentation function FNCODE and add it to SEQ. + If COND_VAR is not NULL, it is a boolean variable guarding the call to + the instrumentation function. IF STMT is not NULL, it is a statement + to be executed just before the call to the instrumentation function. */ + +static void +build_instrumentation_call (gimple_seq *seq, enum built_in_function fncode, + tree cond_var, gimple *stmt) +{ + /* The instrumentation hooks aren't going to call the instrumented + function and the address they receive is expected to be matchable + against symbol addresses. Make sure we don't create a trampoline, + in case the current function is nested. */ + tree this_fn_addr = build_fold_addr_expr (current_function_decl); + TREE_NO_TRAMPOLINE (this_fn_addr) = 1; + + tree label_true, label_false; + if (cond_var) + { + label_true = create_artificial_label (UNKNOWN_LOCATION); + label_false = create_artificial_label (UNKNOWN_LOCATION); + gcond *cond = gimple_build_cond (EQ_EXPR, cond_var, boolean_false_node, + label_true, label_false); + gimplify_seq_add_stmt (seq, cond); + gimplify_seq_add_stmt (seq, gimple_build_label (label_true)); + gimplify_seq_add_stmt (seq, gimple_build_predict (PRED_COLD_LABEL, + NOT_TAKEN)); + } + + if (stmt) + gimplify_seq_add_stmt (seq, stmt); + + tree x = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS); + gcall *call = gimple_build_call (x, 1, integer_zero_node); + tree tmp_var = create_tmp_var (ptr_type_node, "return_addr"); + gimple_call_set_lhs (call, tmp_var); + gimplify_seq_add_stmt (seq, call); + x = builtin_decl_implicit (fncode); + call = gimple_build_call (x, 2, this_fn_addr, tmp_var); + gimplify_seq_add_stmt (seq, call); + + if (cond_var) + gimplify_seq_add_stmt (seq, gimple_build_label (label_false)); +} + /* Entry point to the gimplification pass. FNDECL is the FUNCTION_DECL node for the function we want to gimplify. @@ -16636,40 +16681,66 @@ gimplify_function_tree (tree fndecl) && DECL_DISREGARD_INLINE_LIMITS (fndecl)) && !flag_instrument_functions_exclude_p (fndecl)) { - tree x; - gbind *new_bind; - gimple *tf; - gimple_seq cleanup = NULL, body = NULL; - tree tmp_var, this_fn_addr; - gcall *call; - - /* The instrumentation hooks aren't going to call the instrumented - function and the address they receive is expected to be matchable - against symbol addresses. Make sure we don't create a trampoline, - in case the current function is nested. */ - this_fn_addr = build_fold_addr_expr (current_function_decl); - TREE_NO_TRAMPOLINE (this_fn_addr) = 1; - - x = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS); - call = gimple_build_call (x, 1, integer_zero_node); - tmp_var = create_tmp_var (ptr_type_node, "return_addr"); - gimple_call_set_lhs (call, tmp_var); - gimplify_seq_add_stmt (&cleanup, call); - x = builtin_decl_implicit (BUILT_IN_PROFILE_FUNC_EXIT); - call = gimple_build_call (x, 2, this_fn_addr, tmp_var); - gimplify_seq_add_stmt (&cleanup, call); - tf = gimple_build_try (seq, cleanup, GIMPLE_TRY_FINALLY); - - x = builtin_decl_implicit (BUILT_IN_RETURN_ADDRESS); - call = gimple_build_call (x, 1, integer_zero_node); - tmp_var = create_tmp_var (ptr_type_node, "return_addr"); - gimple_call_set_lhs (call, tmp_var); - gimplify_seq_add_stmt (&body, call); - x = builtin_decl_implicit (BUILT_IN_PROFILE_FUNC_ENTER); - call = gimple_build_call (x, 2, this_fn_addr, tmp_var); - gimplify_seq_add_stmt (&body, call); + gimple_seq body = NULL, cleanup = NULL; + gassign *assign; + tree cond_var; + + /* If -finstrument-functions-once is specified, generate: + + static volatile bool C.0 = false; + bool tmp_called; + + tmp_called = C.0; + if (!tmp_called) + { + C.0 = true; + [call profiling enter function] + } + + without specific protection for data races. */ + if (flag_instrument_function_entry_exit > 1) + { + tree first_var + = build_decl (DECL_SOURCE_LOCATION (current_function_decl), + VAR_DECL, + create_tmp_var_name ("C"), + boolean_type_node); + DECL_ARTIFICIAL (first_var) = 1; + DECL_IGNORED_P (first_var) = 1; + TREE_STATIC (first_var) = 1; + TREE_THIS_VOLATILE (first_var) = 1; + TREE_USED (first_var) = 1; + DECL_INITIAL (first_var) = boolean_false_node; + varpool_node::add (first_var); + + cond_var = create_tmp_var (boolean_type_node, "tmp_called"); + assign = gimple_build_assign (cond_var, first_var); + gimplify_seq_add_stmt (&body, assign); + + assign = gimple_build_assign (first_var, boolean_true_node); + } + + else + { + cond_var = NULL_TREE; + assign = NULL; + } + + build_instrumentation_call (&body, BUILT_IN_PROFILE_FUNC_ENTER, + cond_var, assign); + + /* If -finstrument-functions-once is specified, generate: + + if (!tmp_called) + [call profiling exit function] + + without specific protection for data races. */ + build_instrumentation_call (&cleanup, BUILT_IN_PROFILE_FUNC_EXIT, + cond_var, NULL); + + gimple *tf = gimple_build_try (seq, cleanup, GIMPLE_TRY_FINALLY); gimplify_seq_add_stmt (&body, tf); - new_bind = gimple_build_bind (NULL, body, NULL); + gbind *new_bind = gimple_build_bind (NULL, body, NULL); /* Replace the current function body with the body wrapped in the try/finally TF. */ diff --git a/gcc/testsuite/gcc.dg/instrument-4.c b/gcc/testsuite/gcc.dg/instrument-4.c new file mode 100644 index 00000000000..c19e8bf3971 --- /dev/null +++ b/gcc/testsuite/gcc.dg/instrument-4.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "-finstrument-functions-once" } */ + +void fn () { } + +/* { dg-final { scan-assembler "__cyg_profile_func_enter" } } */ +/* { dg-final { scan-assembler "__cyg_profile_func_exit" } } */