This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PING] [PATCH] testsuite: add coverage for diagnostics relating to inlining (PR tree-optimization/83336)
- From: David Malcolm <dmalcolm at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Mon, 18 Dec 2017 12:14:21 -0500
- Subject: [PING] [PATCH] testsuite: add coverage for diagnostics relating to inlining (PR tree-optimization/83336)
- Authentication-results: sourceware.org; auth=none
- References: <1513025047-19327-1-git-send-email-dmalcolm@redhat.com>
Is the tweak to prune.exp at the end of this patch OK? (I can self-
approve the rest of this).
On Mon, 2017-12-11 at 15:44 -0500, David Malcolm wrote:
> In theory, the diagnostics subsystem can print context information on
> code inlining when diagnostics are emitted by the middle-end,
> describing
> the chain of inlined callsites that led to a particular warning,
> but PR tree-optimization/83336 describes various issues with this.
>
> An underlying issue is that we have very little automated testing for
> this code: gcc.dg/tm/pr52141.c has a test, but in general, prune.exp
> filters out the various "inlined from" lines.
>
> The following patch adds test coverage for it for C and C++ via a new
> testsuite plugin, which emits a warning from the middle-end; the test
> cases use dg-regexp to verify that the "inlined from" lines are
> emitted correctly, with the correct function names and source
> locations.
>
> Doing so requires a change to prune.exp: the dg-regexp lines have to
> be handled *before* the "inlined from" lines are stripped.
>
> (I have various followups, but establishing automated test coverage
> seems like an important first step)
>
> Successfully bootstrapped®rtested on x86_64-pc-linux-gnu; adds
> 7 PASS results to g++.sum and 31 PASS results to gcc.sum.
>
> OK for trunk?
>
> gcc/testsuite/ChangeLog:
> PR tree-optimization/83336
> * g++.dg/cpp0x/missing-initializer_list-include.C: Update for
> changes to prune.exp's handling of dg-regexp.
> * g++.dg/plugin/diagnostic-test-inlining-1.C: New test case.
> * g++.dg/plugin/plugin.exp (plugin_test_list): Add it, via
> gcc.dg's plugin/diagnostic_plugin_test_inlining.c.
> * gcc.dg/plugin/diagnostic-test-inlining-1.c: New test case.
> * gcc.dg/plugin/diagnostic-test-inlining-2.c: Likewise.
> * gcc.dg/plugin/diagnostic-test-inlining-3.c: Likewise.
> * gcc.dg/plugin/diagnostic-test-inlining-4.c: Likewise.
> * gcc.dg/plugin/diagnostic_plugin_test_inlining.c: New test
> plugin.
> * gcc.dg/plugin/plugin.exp (plugin_test_list): Add them.
> * lib/prune.exp (prune_gcc_output): Move call to handle-dg-
> regexps
> to before the various text stripping regsup invocations,
> in particular, to before the stripping of "inlined from".
> ---
> .../cpp0x/missing-initializer_list-include.C | 1 +
> .../g++.dg/plugin/diagnostic-test-inlining-1.C | 34 ++++
> gcc/testsuite/g++.dg/plugin/plugin.exp | 2 +
> .../gcc.dg/plugin/diagnostic-test-inlining-1.c | 34 ++++
> .../gcc.dg/plugin/diagnostic-test-inlining-2.c | 48 ++++++
> .../gcc.dg/plugin/diagnostic-test-inlining-3.c | 43 +++++
> .../gcc.dg/plugin/diagnostic-test-inlining-4.c | 56 +++++++
> .../plugin/diagnostic_plugin_test_inlining.c | 180
> +++++++++++++++++++++
> gcc/testsuite/gcc.dg/plugin/plugin.exp | 5 +
> gcc/testsuite/lib/prune.exp | 6 +-
> 10 files changed, 406 insertions(+), 3 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/plugin/diagnostic-test-
> inlining-1.C
> create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-
> inlining-1.c
> create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-
> inlining-2.c
> create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-
> inlining-3.c
> create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-
> inlining-4.c
> create mode 100644
> gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_inlining.c
>
> diff --git a/gcc/testsuite/g++.dg/cpp0x/missing-initializer_list-
> include.C b/gcc/testsuite/g++.dg/cpp0x/missing-initializer_list-
> include.C
> index 7d72ec4..1010b0a 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/missing-initializer_list-include.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/missing-initializer_list-include.C
> @@ -24,5 +24,6 @@ void test (int i)
> +#include <initializer_list>
> /* This is padding (to avoid the generated patch containing DejaGnu
> directives). */
> +
> { dg-end-multiline-output "" }
> #endif
> diff --git a/gcc/testsuite/g++.dg/plugin/diagnostic-test-inlining-1.C
> b/gcc/testsuite/g++.dg/plugin/diagnostic-test-inlining-1.C
> new file mode 100644
> index 0000000..df7bb1f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/plugin/diagnostic-test-inlining-1.C
> @@ -0,0 +1,34 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wno-attributes -fdiagnostics-show-caret" } */
> +
> +extern void __emit_warning (const char *message);
> +
> +/* Verify that the diagnostic subsytem describes the chain of
> inlining
> + when reporting the warning. */
> +
> +__attribute__((always_inline))
> +static void foo (void)
> +{
> + __emit_warning ("message");
> +}
> +
> +__attribute__((always_inline))
> +static void bar (void)
> +{
> + foo ();
> +}
> +
> +int main()
> +{
> + bar ();
> + return 0;
> +}
> +
> +/* { dg-regexp "In function 'void foo\\(\\)'," "" } */
> +/* { dg-regexp " inlined from 'void bar\\(\\)' at .+/diagnostic-
> test-inlining-1.C:18:7," "" } */
> +/* { dg-regexp " inlined from 'int main\\(\\)' at .+/diagnostic-
> test-inlining-1.C:23:7:" "" } */
> +/* { dg-warning "18: message" "" { target *-*-* } 12 } */
> +/* { dg-begin-multiline-output "" }
> + __emit_warning ("message");
> + ~~~~~~~~~~~~~~~^~~~~~~~~~~
> + { dg-end-multiline-output "" } */
> diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp
> b/gcc/testsuite/g++.dg/plugin/plugin.exp
> index e40cba3..4f9ce16 100644
> --- a/gcc/testsuite/g++.dg/plugin/plugin.exp
> +++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
> @@ -65,6 +65,8 @@ set plugin_test_list [list \
> { def_plugin.c def-plugin-test.C } \
> {
> ../../gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c \
> diagnostic-test-expressions-1.C } \
> + { ../../gcc.dg/plugin/diagnostic_plugin_test_inlining.c \
> + diagnostic-test-inlining-1.C } \
> { show_template_tree_color_plugin.c \
> show-template-tree-color.C \
> show-template-tree-color-no-elide-type.C } \
> diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-1.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-1.c
> new file mode 100644
> index 0000000..a24b7f8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-1.c
> @@ -0,0 +1,34 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wno-attributes -fdiagnostics-show-caret" } */
> +
> +extern void __emit_warning (const char *message);
> +
> +__attribute__((always_inline))
> +static void foo (void)
> +{
> + __emit_warning ("message");
> +}
> +
> +__attribute__((always_inline))
> +static void bar (void)
> +{
> + foo ();
> +}
> +
> +int main()
> +{
> + bar ();
> + return 0;
> +}
> +
> +/* Verify that the diagnostic subsytem describes the chain of
> inlining
> + when reporting the warning. */
> +
> +/* { dg-regexp "In function 'foo'," "" } */
> +/* { dg-regexp " inlined from 'bar' at .+/diagnostic-test-
> inlining-1.c:15:3," "" } */
> +/* { dg-regexp " inlined from 'main' at .+/diagnostic-test-
> inlining-1.c:20:3:" "" } */
> +/* { dg-warning "3: message" "" { target *-*-* } 9 } */
> +/* { dg-begin-multiline-output "" }
> + __emit_warning ("message");
> + ^~~~~~~~~~~~~~~~~~~~~~~~~~
> + { dg-end-multiline-output "" } */
> diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-2.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-2.c
> new file mode 100644
> index 0000000..52e4825
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-2.c
> @@ -0,0 +1,48 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wno-attributes -fdiagnostics-show-caret" } */
> +
> +extern void __emit_warning (const char *message);
> +
> +#define INNER_WARNING(MSG) __emit_warning (MSG)
> +
> +#define OUTER_WARNING(MSG) INNER_WARNING (MSG)
> +
> +__attribute__((always_inline))
> +static void foo (void)
> +{
> + OUTER_WARNING ("message");
> +}
> +
> +__attribute__((always_inline))
> +static void bar (void)
> +{
> + foo ();
> +}
> +
> +int main()
> +{
> + bar ();
> + return 0;
> +}
> +
> +/* Verify that the diagnostic subsytem describes both the chains of
> + inlining and of macro expansion when reporting the warning. */
> +
> +/* { dg-regexp "In function 'foo'," "" } */
> +/* { dg-regexp " inlined from 'bar' at .+/diagnostic-test-
> inlining-2.c:19:3," "" } */
> +/* { dg-regexp " inlined from 'main' at .+/diagnostic-test-
> inlining-2.c:24:3:" "" } */
> +/* { dg-warning "28: message" "" { target c } 6 } */
> +/* { dg-begin-multiline-output "" }
> + #define INNER_WARNING(MSG) __emit_warning (MSG)
> + ^~~~~~~~~~~~~~~~~~~~
> + { dg-end-multiline-output "" } */
> +/* { dg-message "28: in expansion of macro 'INNER_WARNING'" "" {
> target c } 8 } */
> +/* { dg-begin-multiline-output "" }
> + #define OUTER_WARNING(MSG) INNER_WARNING (MSG)
> + ^~~~~~~~~~~~~
> + { dg-end-multiline-output "" } */
> +/* { dg-message "3: in expansion of macro 'OUTER_WARNING'" "" {
> target c } 13 } */
> +/* { dg-begin-multiline-output "" }
> + OUTER_WARNING ("message");
> + ^~~~~~~~~~~~~
> + { dg-end-multiline-output "" } */
> diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-3.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-3.c
> new file mode 100644
> index 0000000..e1a4fca
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-3.c
> @@ -0,0 +1,43 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wno-attributes -fdiagnostics-show-caret -O1" } */
> +
> +extern void __emit_warning (const char *message);
> +
> +__attribute__((always_inline))
> +static void foo (void)
> +{
> + __emit_warning ("message");
> +}
> +
> +__attribute__((always_inline))
> +static void bar (void)
> +{
> + foo ();
> +}
> +
> +int main()
> +{
> + bar ();
> + return 0;
> +}
> +
> +/* Reproducer for PR tree-optimization/83336: when optimization is
> + enabled, but debuginfo isn't, the diagnostics subsystem doesn't
> + report the full inlining chain at a middle-end warning.
> +
> + This is a copy of diagnostic-test-inlining-1.c, but with -O1.
> +
> + Ideally the diagnostics subsystem would report:
> + In function 'foo', inlined from 'bar' at LOC A, inlined from
> 'main' at LOC B:
> + but with -O1 it only reports:
> + In function 'foo', inlined from 'main' at LOC A:
> +
> + This test case captures this behavior. */
> +
> +/* { dg-regexp "In function 'foo'," "" } */
> +/* { dg-regexp " inlined from 'main' at .+/diagnostic-test-
> inlining-3.c:15:3:" "" } */
> +/* { dg-warning "3: message" "" { target *-*-* } 9 } */
> +/* { dg-begin-multiline-output "" }
> + __emit_warning ("message");
> + ^~~~~~~~~~~~~~~~~~~~~~~~~~
> + { dg-end-multiline-output "" } */
> diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-4.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-4.c
> new file mode 100644
> index 0000000..dfb939d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-inlining-4.c
> @@ -0,0 +1,56 @@
> +/* { dg-do compile } */
> +/* { dg-options "-Wno-attributes -fdiagnostics-show-caret" } */
> +
> +extern void __emit_warning (const char *message);
> +
> +__attribute__((always_inline))
> +static void depth_0 (void)
> +{
> + __emit_warning ("message");
> +}
> +
> +__attribute__((always_inline))
> +static void depth_1 (void)
> +{
> + depth_0 ();
> +}
> +
> +__attribute__((always_inline))
> +static void depth_2 (void)
> +{
> + depth_1 ();
> +}
> +
> +__attribute__((always_inline))
> +static void depth_3 (void)
> +{
> + depth_2 ();
> +}
> +
> +__attribute__((always_inline))
> +static void depth_4 (void)
> +{
> + depth_3 ();
> +}
> +
> +int main()
> +{
> + depth_4 ();
> + return 0;
> +}
> +
> +/* Verify that the diagnostic subsytem describes the chain of
> inlining
> + when reporting the warning, for an example showing many levels of
> + inlining. */
> +
> +/* { dg-regexp "In function 'depth_0'," "" } */
> +/* { dg-regexp " inlined from 'depth_1' at .+/diagnostic-test-
> inlining-4.c:15:3," "" } */
> +/* { dg-regexp " inlined from 'depth_2' at .+/diagnostic-test-
> inlining-4.c:21:3," "" } */
> +/* { dg-regexp " inlined from 'depth_3' at .+/diagnostic-test-
> inlining-4.c:27:3," "" } */
> +/* { dg-regexp " inlined from 'depth_4' at .+/diagnostic-test-
> inlining-4.c:33:3," "" } */
> +/* { dg-regexp " inlined from 'main' at .+/diagnostic-test-
> inlining-4.c:38:3:" "" } */
> +/* { dg-warning "3: message" "" { target *-*-* } 9 } */
> +/* { dg-begin-multiline-output "" }
> + __emit_warning ("message");
> + ^~~~~~~~~~~~~~~~~~~~~~~~~~
> + { dg-end-multiline-output "" } */
> diff --git
> a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_inlining.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_inlining.c
> new file mode 100644
> index 0000000..49b78cc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_inlining.c
> @@ -0,0 +1,180 @@
> +/* { dg-options "-O" } */
> +
> +#include "gcc-plugin.h"
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "tm.h"
> +#include "tree.h"
> +#include "stringpool.h"
> +#include "toplev.h"
> +#include "basic-block.h"
> +#include "hash-table.h"
> +#include "vec.h"
> +#include "ggc.h"
> +#include "basic-block.h"
> +#include "tree-ssa-alias.h"
> +#include "internal-fn.h"
> +#include "gimple-fold.h"
> +#include "tree-eh.h"
> +#include "gimple-expr.h"
> +#include "is-a.h"
> +#include "gimple.h"
> +#include "gimple-iterator.h"
> +#include "tree.h"
> +#include "tree-pass.h"
> +#include "intl.h"
> +#include "plugin-version.h"
> +#include "c-family/c-common.h"
> +#include "diagnostic.h"
> +#include "context.h"
> +#include "print-tree.h"
> +#include "cpplib.h"
> +#include "c-family/c-pragma.h"
> +#include "substring-locations.h"
> +
> +int plugin_is_GPL_compatible;
> +
> +/* A custom pass for emitting dummy warnings from the middle-
> end. */
> +
> +const pass_data pass_data_test_inlining =
> +{
> + GIMPLE_PASS, /* type */
> + "test_inlining", /* name */
> + OPTGROUP_NONE, /* optinfo_flags */
> + TV_NONE, /* tv_id */
> + PROP_ssa, /* properties_required */
> + 0, /* properties_provided */
> + 0, /* properties_destroyed */
> + 0, /* todo_flags_start */
> + 0, /* todo_flags_finish */
> +};
> +
> +class pass_test_inlining : public gimple_opt_pass
> +{
> +public:
> + pass_test_inlining(gcc::context *ctxt)
> + : gimple_opt_pass(pass_data_test_inlining, ctxt)
> + {}
> +
> + /* opt_pass methods: */
> + bool gate (function *) { return true; }
> + virtual unsigned int execute (function *);
> +
> +}; // class pass_test_inlining
> +
> +/* Determine if STMT is a call with NUM_ARGS arguments to a function
> + named FUNCNAME.
> + If so, return STMT as a gcall *. Otherwise return NULL. */
> +
> +static gcall *
> +check_for_named_call (gimple *stmt,
> + const char *funcname, unsigned int num_args)
> +{
> + gcc_assert (funcname);
> +
> + gcall *call = dyn_cast <gcall *> (stmt);
> + if (!call)
> + return NULL;
> +
> + tree fndecl = gimple_call_fndecl (call);
> + if (!fndecl)
> + return NULL;
> +
> + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname))
> + return NULL;
> +
> + if (gimple_call_num_args (call) != num_args)
> + {
> + error_at (stmt->location, "expected number of args: %i (got
> %i)",
> + num_args, gimple_call_num_args (call));
> + return NULL;
> + }
> +
> + return call;
> +}
> +
> +/* Emit a warning at LOC. */
> +
> +static void
> +emit_warning (location_t loc)
> +{
> + source_range src_range = get_range_from_loc (line_table, loc);
> + warning_at (loc, 0, "range %i:%i-%i:%i",
> + LOCATION_LINE (src_range.m_start),
> + LOCATION_COLUMN (src_range.m_start),
> + LOCATION_LINE (src_range.m_finish),
> + LOCATION_COLUMN (src_range.m_finish));
> +}
> +
> +/* Code for simulating the emission of a warning from the middle-
> end.
> + Emit a warning for each call to a function named
> "__emit_warning". */
> +
> +static void
> +test_inlining (gimple *stmt)
> +{
> + gcall *call = check_for_named_call (stmt, "__emit_warning", 1);
> + if (!call)
> + return;
> +
> + /* We expect an ADDR_EXPR with a STRING_CST inside it for the
> + initial arg. */
> + tree t_addr_string = gimple_call_arg (call, 0);
> + if (TREE_CODE (t_addr_string) != ADDR_EXPR)
> + {
> + error_at (call->location, "string literal required for arg
> 1");
> + return;
> + }
> +
> + tree t_string = TREE_OPERAND (t_addr_string, 0);
> + if (TREE_CODE (t_string) != STRING_CST)
> + {
> + error_at (call->location, "string literal required for arg
> 1");
> + return;
> + }
> +
> + warning_at (call->location, 0, "%G%s", call,
> + TREE_STRING_POINTER (t_string));
> +}
> +
> +/* Call test_inlining on every statement within FUN. */
> +
> +unsigned int
> +pass_test_inlining::execute (function *fun)
> +{
> + gimple_stmt_iterator gsi;
> + basic_block bb;
> +
> + FOR_EACH_BB_FN (bb, fun)
> + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> + {
> + gimple *stmt = gsi_stmt (gsi);
> + test_inlining (stmt);
> + }
> +
> + return 0;
> +}
> +
> +/* Entrypoint for the plugin. Create and register the custom
> pass. */
> +
> +int
> +plugin_init (struct plugin_name_args *plugin_info,
> + struct plugin_gcc_version *version)
> +{
> + struct register_pass_info pass_info;
> + const char *plugin_name = plugin_info->base_name;
> + int argc = plugin_info->argc;
> + struct plugin_argument *argv = plugin_info->argv;
> +
> + if (!plugin_default_version_check (version, &gcc_version))
> + return 1;
> +
> + pass_info.pass = new pass_test_inlining (g);
> + pass_info.reference_pass_name = "*warn_function_noreturn";
> + pass_info.ref_pass_instance_number = 1;
> + pass_info.pos_op = PASS_POS_INSERT_AFTER;
> + register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
> + &pass_info);
> +
> + return 0;
> +}
> diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp
> b/gcc/testsuite/gcc.dg/plugin/plugin.exp
> index ff3c976..9da17d0 100644
> --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
> +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
> @@ -76,6 +76,11 @@ set plugin_test_list [list \
> diagnostic-test-string-literals-2.c \
> diagnostic-test-string-literals-3.c \
> diagnostic-test-string-literals-4.c } \
> + { diagnostic_plugin_test_inlining.c \
> + diagnostic-test-inlining-1.c \
> + diagnostic-test-inlining-2.c \
> + diagnostic-test-inlining-3.c \
> + diagnostic-test-inlining-4.c } \
> { location_overflow_plugin.c \
> location-overflow-test-1.c \
> location-overflow-test-2.c } \
> diff --git a/gcc/testsuite/lib/prune.exp
> b/gcc/testsuite/lib/prune.exp
> index afc1a69..b771da2 100644
> --- a/gcc/testsuite/lib/prune.exp
> +++ b/gcc/testsuite/lib/prune.exp
> @@ -28,6 +28,9 @@ proc prune_gcc_output { text } {
>
> #send_user "Before:$text\n"
>
> + # Handle any freeform regexps.
> + set text [handle-dg-regexps $text]
> +
> regsub -all "(^|\n)(\[^\n\]*: )?In ((static member |lambda
> )?function|member|method|(copy
> )?constructor|destructor|instantiation|substitution|program|subroutin
> e|block-data)\[^\n\]*" $text "" text
> regsub -all "(^|\n)\[^\n\]*(: )?At (top level|global
> scope):\[^\n\]*" $text "" text
> regsub -all "(^|\n)\[^\n\]*: (recursively )?required \[^\n\]*"
> $text "" text
> @@ -73,9 +76,6 @@ proc prune_gcc_output { text } {
> # Call into multiline.exp to handle any multiline output
> directives.
> set text [handle-multiline-outputs $text]
>
> - # Handle any freeform regexps.
> - set text [handle-dg-regexps $text]
> -
> #send_user "After:$text\n"
>
> return $text