1 /* Rich optional information on why an optimization wasn't possible.
2    Copyright (C) 2018-2021 Free Software Foundation, Inc.
3    Contributed by David Malcolm <dmalcolm@redhat.com>.
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11 
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along 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 
opt_problem(const dump_location_t & loc,const char * fmt,va_list * ap)43 opt_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 
81 void
emit_and_clear()82 opt_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 
94 opt_problem *opt_problem::s_the_problem;
95 
96 #if CHECKING_P
97 
98 namespace selftest {
99 
100 static opt_result
function_that_succeeds()101 function_that_succeeds ()
102 {
103   return opt_result::success ();
104 }
105 
106 /* Verify that opt_result::success works.  */
107 
108 static void
test_opt_result_success()109 test_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 
143 static opt_result
function_that_fails(const greturn * stmt)144 function_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 
159 static opt_result
function_that_indirectly_fails(const greturn * stmt)160 function_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 
176 static void
test_opt_result_failure_at(const line_table_case & case_)177 test_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 
326 void
opt_problem_cc_tests()327 opt_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