From patchwork Tue May 24 10:48:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Botcazou X-Patchwork-Id: 54338 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id BE6BD3851406 for ; Tue, 24 May 2022 10:49:10 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BE6BD3851406 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1653389350; bh=dsJWo9OsCrsYSBOHPvEbCtleti6wZiD5TL3RiWORDj4=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=EfiqlkTtt5HziOZM64hDp5+PNFL00m4I4dhl1208gfW8H+EBu5HosGpYHhyL5H8ER BKpw9jhtvUTEU0b697dIGc0WVyNj+MRvZAHOeOGk6CLqnWjvfU2fxQr0N6Sth4WIQk 0i4F6hrDZ88cItdYR/iehZfnOu4yV0sge3+RzQzQ= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-x435.google.com (mail-wr1-x435.google.com [IPv6:2a00:1450:4864:20::435]) by sourceware.org (Postfix) with ESMTPS id 1F6F93857409 for ; Tue, 24 May 2022 10:48:38 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 1F6F93857409 Received: by mail-wr1-x435.google.com with SMTP id t13so5938784wrg.9 for ; Tue, 24 May 2022 03:48:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:mime-version :content-transfer-encoding; bh=dsJWo9OsCrsYSBOHPvEbCtleti6wZiD5TL3RiWORDj4=; b=5gLPlUcKt7kNlTpMYodJtAq2tSQJgKiPBoGQzabGyQKvhEfKF2ZtaacVWmvvDoVdt9 S66y/ukQhjaVNimcryeZ/L7nySQ/VpkMmctKbV8Dp19fcN24TAcT/Brgatx8yBS0bsoT Uk9XSIo+hc3xzHlKSfLjUN6sa2eX8s3DTLngPSRibjRDZR73S6xVR5wTBogXihrrhhDU BnwD4YFtMVjH1Q4rCma4TM/+mKh4Hww7OsWPdt9qJGsQLtZO0K2sbK9YBLVmb6t5oYvu foIudLKWmF0G3CvuA5FMsbykcisCuWcLzVAUUCa/TDdF92MV58+X2c4ZhKPuTDKlh79Z +48w== X-Gm-Message-State: AOAM530yMC/qfeq733e5BvWRY0ZMwfNSibd5DVSoI3SRA6Fr9ad3rZBw IpJx2CXy/8WTTohth3fS9S5tLzD/l+Pw1g== X-Google-Smtp-Source: ABdhPJyaQ+UCxsrD+t6KovkiuY0jm3SkiadnP3VyQRp85XierB5W+0r0EMKXsh5mo3QvBUbNCkFKQg== X-Received: by 2002:a5d:6488:0:b0:203:b628:70d2 with SMTP id o8-20020a5d6488000000b00203b62870d2mr22830502wri.83.1653389316795; Tue, 24 May 2022 03:48:36 -0700 (PDT) Received: from fomalhaut.localnet ([2a01:e0a:8d5:d990:bf38:f508:6f40:de1d]) by smtp.gmail.com with ESMTPSA id n2-20020adfc602000000b0020fe35aec4bsm5289316wrg.70.2022.05.24.03.48.35 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 May 2022 03:48:36 -0700 (PDT) X-Google-Original-From: Eric Botcazou To: gcc-patches@gcc.gnu.org Subject: [PATCH] Introduce -finstrument-functions-once Date: Tue, 24 May 2022 12:48:34 +0200 Message-ID: <4713782.GXAFRqVoOG@fomalhaut> MIME-Version: 1.0 X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Eric Botcazou via Gcc-patches From: Eric Botcazou Reply-To: Eric Botcazou Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Hi, some time ago we were requested to implement a -finstrument-functions-once switch in the compiler, with the semantics that the profiling functions be called only once per instrumented function. The goal was 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. Tested on x86-64/Linux, OK for the mainline? 2022-05-24 Eric Botcazou * common.opt (finstrument-functions): Set explicit value. (-finstrument-functions-once): New option. * doc/invoke.texi (Program Instrumentation Options): Document it. * gimplify.c (build_instrumentation_call): New static function. (gimplify_function_tree): Invoke it to emit the instrumentation calls if -finstrument-functions[-once] is specified. diff --git a/gcc/common.opt b/gcc/common.opt index 8a0dafc522d..c82df1778e6 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1878,9 +1878,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 e8e6d4e039b..eaea3a7cb93 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -617,7 +617,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} @@ -16365,6 +16365,20 @@ 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 invoked at @emph{least} once per process and at @emph{most} +once per thread. + @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 260993be215..8ce15a2adad 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -16570,6 +16570,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_true_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. @@ -16620,40 +16665,60 @@ 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); + tree cond_var = NULL_TREE; + gimple_seq body = NULL, cleanup = NULL; + gassign *assign = NULL; + + /* If -finstrument-functions-once is specified, generate: + + static volatile bool F.0 = true; + bool tmp_first; + + tmp_first = F.0; + if (tmp_first) + { + F.0 = false; + [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 ("F"), + 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_true_node; + varpool_node::add (first_var); + + cond_var = create_tmp_var (boolean_type_node, "tmp_first"); + assign = gimple_build_assign (cond_var, first_var); + gimplify_seq_add_stmt (&body, assign); + + assign = gimple_build_assign (first_var, boolean_false_node); + } + + build_instrumentation_call (&body, BUILT_IN_PROFILE_FUNC_ENTER, + cond_var, assign); + + /* If -finstrument-functions-once is specified, generate: + + if (tmp_first) + [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. */