From ab6e54a684f6fcbf9eed989d170905fd889e19cc Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 18 Dec 2017 19:23:30 +0000 Subject: [PATCH] testsuite: add coverage for diagnostics relating to inlining (PR tree-optimization/83336) 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. 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". From-SVN: r255786 --- gcc/testsuite/ChangeLog | 19 ++ .../cpp0x/missing-initializer_list-include.C | 1 + .../plugin/diagnostic-test-inlining-1.C | 34 ++++ gcc/testsuite/g++.dg/plugin/plugin.exp | 2 + .../plugin/diagnostic-test-inlining-1.c | 34 ++++ .../plugin/diagnostic-test-inlining-2.c | 48 +++++ .../plugin/diagnostic-test-inlining-3.c | 43 +++++ .../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 +- 11 files changed, 425 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/ChangeLog b/gcc/testsuite/ChangeLog index 6e64c42cd0c..ae1fd01fb79 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,22 @@ +2017-12-18 David Malcolm + + 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". + 2017-12-18 Jeff Law PR middle-end/83460 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 7d72ec45de4..1010b0a4edd 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 /* 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 00000000000..df7bb1f856c --- /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 e40cba328c5..4f9ce16ffc9 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 00000000000..a24b7f890db --- /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 00000000000..52e482575a9 --- /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 00000000000..e1a4fca2cb4 --- /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 00000000000..dfb939db17d --- /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 00000000000..49b78ccf1fc --- /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 (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 057f4e223df..81dee2248fe 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -83,6 +83,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 afc1a696393..b771da2cb9a 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|subroutine|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 -- 2.30.2