1/* Rich optional information on why an optimization wasn't possible.
2   Copyright (C) 2018-2020 Free Software Foundation, Inc.
3   Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3.  If not see
19<http://www.gnu.org/licenses/>.  */
20
21#include "config.h"
22#include "system.h"
23#include "coretypes.h"
24#include "backend.h"
25#include "tree.h"
26#include "gimple.h"
27#include "pretty-print.h"
28#include "opt-problem.h"
29#include "dump-context.h"
30#include "tree-pass.h"
31#include "selftest.h"
32
33/* opt_problem's ctor.
34
35   Use FMT and AP to emit a message to the "immediate" dump destinations
36   as if via:
37     dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, ...)
38
39   The optinfo_item instances are not emitted yet.  Instead, they
40   are retained internally so that the message can be replayed and
41   emitted when this problem is handled, higher up the call stack.  */
42
43opt_problem::opt_problem (const dump_location_t &loc,
44			  const char *fmt, va_list *ap)
45: m_optinfo (loc, OPTINFO_KIND_FAILURE, current_pass)
46{
47  /* We shouldn't be bothering to construct these objects if
48     dumping isn't enabled.  */
49  gcc_assert (dump_enabled_p ());
50
51  /* Update the singleton.  */
52  delete s_the_problem;
53  s_the_problem = this;
54
55  /* Print the location to the "immediate" dump destinations.  */
56  dump_context &dc = dump_context::get ();
57  dc.dump_loc (MSG_MISSED_OPTIMIZATION, loc.get_user_location ());
58
59  /* Print the formatted string to this opt_problem's optinfo, dumping
60     the items to the "immediate" dump destinations, and storing items
61     for later retrieval.  */
62  {
63    dump_pretty_printer pp (&dump_context::get (), MSG_MISSED_OPTIMIZATION);
64
65    text_info text;
66    text.err_no = errno;
67    text.args_ptr = ap;
68    text.format_spec = fmt; /* No i18n is performed.  */
69
70    /* Phases 1 and 2, using pp_format.  */
71    pp_format (&pp, &text);
72
73    /* Phase 3: dump the items to the "immediate" dump destinations,
74       and storing them into m_optinfo for later retrieval.  */
75    pp.emit_items (&m_optinfo);
76  }
77}
78
79/* Emit this problem and delete it, clearing the current opt_problem.  */
80
81void
82opt_problem::emit_and_clear ()
83{
84  gcc_assert (this == s_the_problem);
85
86  m_optinfo.emit_for_opt_problem ();
87
88  delete this;
89  s_the_problem = NULL;
90}
91
92/* The singleton opt_problem *.  */
93
94opt_problem *opt_problem::s_the_problem;
95
96#if CHECKING_P
97
98namespace selftest {
99
100static opt_result
101function_that_succeeds ()
102{
103  return opt_result::success ();
104}
105
106/* Verify that opt_result::success works.  */
107
108static void
109test_opt_result_success ()
110{
111  /* Run all tests twice, with and then without dumping enabled.  */
112  for (int i = 0 ; i < 2; i++)
113    {
114      bool with_dumping = (i == 0);
115
116      temp_dump_context tmp (with_dumping, with_dumping,
117			     MSG_ALL_KINDS | MSG_ALL_PRIORITIES);
118
119      if (with_dumping)
120	gcc_assert (dump_enabled_p ());
121      else
122	gcc_assert (!dump_enabled_p ());
123
124      opt_result res = function_that_succeeds ();
125
126      /* Verify that "success" can be used as a "true" boolean.  */
127      ASSERT_TRUE (res);
128
129      /* Verify the underlying opt_wrapper<bool>.  */
130      ASSERT_TRUE (res.get_result ());
131      ASSERT_EQ (res.get_problem (), NULL);
132
133      /* Nothing should have been dumped.  */
134      ASSERT_DUMPED_TEXT_EQ (tmp, "");
135      optinfo *info = tmp.get_pending_optinfo ();
136      ASSERT_EQ (info, NULL);
137    }
138}
139
140/* Example of a function that fails, with a non-trivial
141   pre-canned error message.  */
142
143static opt_result
144function_that_fails (const greturn *stmt)
145{
146  gcc_assert (stmt);
147  gcc_assert (gimple_return_retval (stmt));
148
149  AUTO_DUMP_SCOPE ("function_that_fails", stmt);
150
151  return opt_result::failure_at (stmt,
152				 "can't handle return type: %T for stmt: %G",
153				 TREE_TYPE (gimple_return_retval (stmt)),
154				 static_cast <const gimple *> (stmt));
155}
156
157/* Example of a function that indirectly fails.  */
158
159static opt_result
160function_that_indirectly_fails (const greturn *stmt)
161{
162  AUTO_DUMP_SCOPE ("function_that_indirectly_fails", stmt);
163
164  opt_result res = function_that_fails (stmt);
165  if (!res)
166    return res;
167  return opt_result::success ();
168}
169
170/* Verify that opt_result::failure_at works.
171   Simulate a failure handling a stmt at one location whilst considering
172   an optimization that's notionally at another location (as a microcosm
173   of e.g. a problematic statement within a loop that prevents loop
174   vectorization).  */
175
176static void
177test_opt_result_failure_at (const line_table_case &case_)
178{
179  /* Generate a location_t for testing.  */
180  line_table_test ltt (case_);
181  const line_map_ordinary *ord_map
182    = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
183					   "test.c", 0));
184  linemap_line_start (line_table, 5, 100);
185
186  /* A test location: "test.c:5:10".  */
187  const location_t line_5 = linemap_position_for_column (line_table, 10);
188
189  /* Another test location: "test.c:6:12".  */
190  const location_t line_6
191    = linemap_position_for_line_and_column (line_table, ord_map, 6, 12);
192
193  if (line_6 > LINE_MAP_MAX_LOCATION_WITH_COLS)
194    return;
195
196  /* Generate statements using "line_5" and "line_6" for testing.  */
197  greturn *stmt_at_5 = gimple_build_return (integer_one_node);
198  gimple_set_location (stmt_at_5, line_5);
199
200  greturn *stmt_at_6 = gimple_build_return (integer_zero_node);
201  gimple_set_location (stmt_at_6, line_6);
202
203  /* Run with and then without dumping enabled.  */
204  for (int i = 0; i < 2; i++)
205    {
206      bool with_dumping = (i == 0);
207
208      /* Run with all 4 combinations of
209	 with and without MSG_PRIORITY_INTERNALS and
210	 with and without MSG_PRIORITY_REEMITTED.  */
211      for (int j = 0; j < 4; j++)
212	{
213	  dump_flags_t filter = MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING;
214	  if (j / 2)
215	    filter |= MSG_PRIORITY_INTERNALS;
216	  if (j % 2)
217	    filter |= MSG_PRIORITY_REEMITTED;
218
219	  temp_dump_context tmp (with_dumping, with_dumping, filter);
220
221	  if (with_dumping)
222	    gcc_assert (dump_enabled_p ());
223	  else
224	    gcc_assert (!dump_enabled_p ());
225
226	  /* Simulate attempting to optimize "stmt_at_6".  */
227	  opt_result res = function_that_indirectly_fails (stmt_at_6);
228
229	  /* Verify that "failure" can be used as a "false" boolean.  */
230	  ASSERT_FALSE (res);
231
232	  /* Verify the underlying opt_wrapper<bool>.  */
233	  ASSERT_FALSE (res.get_result ());
234	  opt_problem *problem = res.get_problem ();
235
236	  if (with_dumping)
237	    {
238	      ASSERT_NE (problem, NULL);
239	      ASSERT_EQ (problem->get_dump_location ().get_location_t (),
240			 line_6);
241#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
242	      /* Verify that the problem captures the implementation location
243		 it was emitted from.  */
244	      const dump_impl_location_t &impl_location
245		= problem->get_dump_location ().get_impl_location ();
246	      ASSERT_STR_CONTAINS (impl_location.m_function,
247				   "function_that_fails");
248#endif
249
250	      /* Verify that the underlying dump items are retained in the
251		 opt_problem.  */
252	      const optinfo &info = problem->get_optinfo ();
253	      ASSERT_EQ (info.get_dump_location ().get_location_t (), line_6);
254	      ASSERT_EQ (info.num_items (), 4);
255	      ASSERT_IS_TEXT (info.get_item (0), "can't handle return type: ");
256	      ASSERT_IS_TREE (info.get_item (1), UNKNOWN_LOCATION, "int");
257	      ASSERT_IS_TEXT (info.get_item (2), " for stmt: ");
258	      ASSERT_IS_GIMPLE (info.get_item (3), line_6, "return 0;\n");
259
260	      /* ...but not in the dump_context's pending_optinfo.  */
261	      ASSERT_EQ (tmp.get_pending_optinfo (), NULL);
262
263	      /* Simulate emitting a high-level summary message, followed
264		 by the problem.  */
265	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt_at_5,
266			       "can't optimize loop\n");
267	      problem->emit_and_clear ();
268	      ASSERT_EQ (res.get_problem (), NULL);
269
270	      /* Verify that the error message was dumped (when the failure
271		 occurred).  We can't use a switch here as not all of the
272		 values are const expressions (using C++98).  */
273	      dump_flags_t effective_filter
274		= filter & (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED);
275	      if (effective_filter
276		  == (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED))
277		/* The -fopt-info-internals case.  */
278		ASSERT_DUMPED_TEXT_EQ
279		  (tmp,
280		   "test.c:6:12: note:  === function_that_indirectly_fails"
281		   " ===\n"
282		   "test.c:6:12: note:   === function_that_fails ===\n"
283		   "test.c:6:12: missed:   can't handle return type: int"
284		   " for stmt: return 0;\n"
285		   "test.c:5:10: missed: can't optimize loop\n"
286		   "test.c:6:12: missed: can't handle return type: int"
287		   " for stmt: return 0;\n");
288	      else if (effective_filter == MSG_PRIORITY_INTERNALS)
289		/* The default for dump files.  */
290		ASSERT_DUMPED_TEXT_EQ
291		  (tmp,
292		   "test.c:6:12: note:  === function_that_indirectly_fails"
293		   " ===\n"
294		   "test.c:6:12: note:   === function_that_fails ===\n"
295		   "test.c:6:12: missed:   can't handle return type: int"
296		     " for stmt: return 0;\n"
297		   "test.c:5:10: missed: can't optimize loop\n");
298	      else if (effective_filter == MSG_PRIORITY_REEMITTED)
299		/* The default when -fopt-info is enabled.  */
300		ASSERT_DUMPED_TEXT_EQ
301		  (tmp,
302		   "test.c:5:10: missed: can't optimize loop\n"
303		   "test.c:6:12: missed: can't handle return type: int"
304		   " for stmt: return 0;\n");
305	      else
306		{
307		  gcc_assert (effective_filter == 0);
308		  ASSERT_DUMPED_TEXT_EQ
309		    (tmp,
310		     "test.c:5:10: missed: can't optimize loop\n");
311		}
312	    }
313	  else
314	    {
315	      /* If dumping was disabled, then no problem should have been
316		 created, and nothing should have been dumped.  */
317	      ASSERT_EQ (problem, NULL);
318	      ASSERT_DUMPED_TEXT_EQ (tmp, "");
319	    }
320	}
321    }
322}
323
324/* Run all of the selftests within this file.  */
325
326void
327opt_problem_cc_tests ()
328{
329  test_opt_result_success ();
330  for_each_line_table_case (test_opt_result_failure_at);
331}
332
333} // namespace selftest
334
335#endif /* CHECKING_P */
336