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