Branch data Line data Source code
1 : : /* Rich optional information on why an optimization wasn't possible.
2 : : Copyright (C) 2018-2025 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 : :
43 : 32722 : opt_problem::opt_problem (const dump_location_t &loc,
44 : : const char *fmt, va_list *ap)
45 : 32722 : : 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 : 32722 : gcc_assert (dump_enabled_p ());
50 : :
51 : : /* Update the singleton. */
52 : 58468 : delete s_the_problem;
53 : 32722 : s_the_problem = this;
54 : :
55 : : /* Print the location to the "immediate" dump destinations. */
56 : 32722 : dump_context &dc = dump_context::get ();
57 : 32722 : 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 : 32722 : {
63 : 32722 : dump_pretty_printer pp (&dump_context::get (), MSG_MISSED_OPTIMIZATION);
64 : :
65 : 32722 : text_info text (fmt, /* No i18n is performed. */
66 : 32722 : ap, errno);
67 : :
68 : : /* Phases 1 and 2, using pp_format. */
69 : 32722 : pp_format (&pp, &text);
70 : :
71 : : /* Phase 3: dump the items to the "immediate" dump destinations,
72 : : and storing them into m_optinfo for later retrieval. */
73 : 32722 : pp.set_optinfo (&m_optinfo);
74 : 32722 : pp_output_formatted_text (&pp, nullptr);
75 : 32722 : }
76 : 32722 : }
77 : :
78 : : /* Emit this problem and delete it, clearing the current opt_problem. */
79 : :
80 : : void
81 : 5576 : opt_problem::emit_and_clear ()
82 : : {
83 : 5576 : gcc_assert (this == s_the_problem);
84 : :
85 : 5576 : m_optinfo.emit_for_opt_problem ();
86 : :
87 : 5576 : delete this;
88 : 5576 : s_the_problem = NULL;
89 : 5576 : }
90 : :
91 : : /* The singleton opt_problem *. */
92 : :
93 : : opt_problem *opt_problem::s_the_problem;
94 : :
95 : : #if CHECKING_P
96 : :
97 : : namespace selftest {
98 : :
99 : : static opt_result
100 : 6 : function_that_succeeds ()
101 : : {
102 : 6 : return opt_result::success ();
103 : : }
104 : :
105 : : /* Verify that opt_result::success works. */
106 : :
107 : : static void
108 : 3 : test_opt_result_success ()
109 : : {
110 : : /* Run all tests twice, with and then without dumping enabled. */
111 : 9 : for (int i = 0 ; i < 2; i++)
112 : : {
113 : 6 : bool with_dumping = (i == 0);
114 : :
115 : 6 : temp_dump_context tmp (with_dumping, with_dumping,
116 : 6 : MSG_ALL_KINDS | MSG_ALL_PRIORITIES);
117 : :
118 : 6 : if (with_dumping)
119 : 3 : gcc_assert (dump_enabled_p ());
120 : : else
121 : 3 : gcc_assert (!dump_enabled_p ());
122 : :
123 : 6 : opt_result res = function_that_succeeds ();
124 : :
125 : : /* Verify that "success" can be used as a "true" boolean. */
126 : 6 : ASSERT_TRUE (res);
127 : :
128 : : /* Verify the underlying opt_wrapper<bool>. */
129 : 6 : ASSERT_TRUE (res.get_result ());
130 : 6 : ASSERT_EQ (res.get_problem (), NULL);
131 : :
132 : : /* Nothing should have been dumped. */
133 : 6 : ASSERT_DUMPED_TEXT_EQ (tmp, "");
134 : 6 : optinfo *info = tmp.get_pending_optinfo ();
135 : 6 : ASSERT_EQ (info, NULL);
136 : 6 : }
137 : 3 : }
138 : :
139 : : /* Example of a function that fails, with a non-trivial
140 : : pre-canned error message. */
141 : :
142 : : static opt_result
143 : 336 : function_that_fails (const greturn *stmt)
144 : : {
145 : 336 : gcc_assert (stmt);
146 : 336 : gcc_assert (gimple_return_retval (stmt));
147 : :
148 : 336 : AUTO_DUMP_SCOPE ("function_that_fails", stmt);
149 : :
150 : 336 : return opt_result::failure_at (stmt,
151 : : "can't handle return type: %T for stmt: %G",
152 : 336 : TREE_TYPE (gimple_return_retval (stmt)),
153 : 336 : static_cast <const gimple *> (stmt));
154 : : }
155 : :
156 : : /* Example of a function that indirectly fails. */
157 : :
158 : : static opt_result
159 : 336 : function_that_indirectly_fails (const greturn *stmt)
160 : : {
161 : 336 : AUTO_DUMP_SCOPE ("function_that_indirectly_fails", stmt);
162 : :
163 : 336 : opt_result res = function_that_fails (stmt);
164 : 336 : if (!res)
165 : 336 : return res;
166 : 0 : return opt_result::success ();
167 : : }
168 : :
169 : : /* Verify that opt_result::failure_at works.
170 : : Simulate a failure handling a stmt at one location whilst considering
171 : : an optimization that's notionally at another location (as a microcosm
172 : : of e.g. a problematic statement within a loop that prevents loop
173 : : vectorization). */
174 : :
175 : : static void
176 : 72 : test_opt_result_failure_at (const line_table_case &case_)
177 : : {
178 : : /* Generate a location_t for testing. */
179 : 72 : line_table_test ltt (case_);
180 : 72 : const line_map_ordinary *ord_map
181 : 72 : = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
182 : : "test.c", 0));
183 : 72 : linemap_line_start (line_table, 5, 100);
184 : :
185 : : /* A test location: "test.c:5:10". */
186 : 72 : const location_t line_5 = linemap_position_for_column (line_table, 10);
187 : :
188 : : /* Another test location: "test.c:6:12". */
189 : 72 : const location_t line_6
190 : 72 : = linemap_position_for_line_and_column (line_table, ord_map, 6, 12);
191 : :
192 : 72 : if (line_6 > LINE_MAP_MAX_LOCATION_WITH_COLS)
193 : 30 : return;
194 : :
195 : : /* Generate statements using "line_5" and "line_6" for testing. */
196 : 42 : greturn *stmt_at_5 = gimple_build_return (integer_one_node);
197 : 42 : gimple_set_location (stmt_at_5, line_5);
198 : :
199 : 42 : greturn *stmt_at_6 = gimple_build_return (integer_zero_node);
200 : 42 : gimple_set_location (stmt_at_6, line_6);
201 : :
202 : : /* Run with and then without dumping enabled. */
203 : 126 : for (int i = 0; i < 2; i++)
204 : : {
205 : 84 : bool with_dumping = (i == 0);
206 : :
207 : : /* Run with all 4 combinations of
208 : : with and without MSG_PRIORITY_INTERNALS and
209 : : with and without MSG_PRIORITY_REEMITTED. */
210 : 420 : for (int j = 0; j < 4; j++)
211 : : {
212 : 336 : dump_flags_t filter = MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING;
213 : 336 : if (j / 2)
214 : 168 : filter |= MSG_PRIORITY_INTERNALS;
215 : 336 : if (j % 2)
216 : 168 : filter |= MSG_PRIORITY_REEMITTED;
217 : :
218 : 336 : temp_dump_context tmp (with_dumping, with_dumping, filter);
219 : :
220 : 336 : if (with_dumping)
221 : 168 : gcc_assert (dump_enabled_p ());
222 : : else
223 : 168 : gcc_assert (!dump_enabled_p ());
224 : :
225 : : /* Simulate attempting to optimize "stmt_at_6". */
226 : 336 : opt_result res = function_that_indirectly_fails (stmt_at_6);
227 : :
228 : : /* Verify that "failure" can be used as a "false" boolean. */
229 : 336 : ASSERT_FALSE (res);
230 : :
231 : : /* Verify the underlying opt_wrapper<bool>. */
232 : 336 : ASSERT_FALSE (res.get_result ());
233 : 336 : opt_problem *problem = res.get_problem ();
234 : :
235 : 336 : if (with_dumping)
236 : : {
237 : 168 : ASSERT_NE (problem, NULL);
238 : 168 : ASSERT_EQ (problem->get_dump_location ().get_location_t (),
239 : : line_6);
240 : : #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
241 : : /* Verify that the problem captures the implementation location
242 : : it was emitted from. */
243 : 168 : const dump_impl_location_t &impl_location
244 : 168 : = problem->get_dump_location ().get_impl_location ();
245 : 168 : ASSERT_STR_CONTAINS (impl_location.m_function,
246 : : "function_that_fails");
247 : : #endif
248 : :
249 : : /* Verify that the underlying dump items are retained in the
250 : : opt_problem. */
251 : 168 : const optinfo &info = problem->get_optinfo ();
252 : 168 : ASSERT_EQ (info.get_dump_location ().get_location_t (), line_6);
253 : 168 : ASSERT_EQ (info.num_items (), 4);
254 : 168 : ASSERT_IS_TEXT (info.get_item (0), "can't handle return type: ");
255 : 168 : ASSERT_IS_TREE (info.get_item (1), UNKNOWN_LOCATION, "int");
256 : 168 : ASSERT_IS_TEXT (info.get_item (2), " for stmt: ");
257 : 168 : ASSERT_IS_GIMPLE (info.get_item (3), line_6, "return 0;\n");
258 : :
259 : : /* ...but not in the dump_context's pending_optinfo. */
260 : 168 : ASSERT_EQ (tmp.get_pending_optinfo (), NULL);
261 : :
262 : : /* Simulate emitting a high-level summary message, followed
263 : : by the problem. */
264 : 168 : dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt_at_5,
265 : : "can't optimize loop\n");
266 : 168 : problem->emit_and_clear ();
267 : 168 : ASSERT_EQ (res.get_problem (), NULL);
268 : :
269 : : /* Verify that the error message was dumped (when the failure
270 : : occurred). We can't use a switch here as not all of the
271 : : values are const expressions (using C++98). */
272 : 168 : dump_flags_t effective_filter
273 : 168 : = filter & (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED);
274 : 168 : if (effective_filter
275 : 168 : == (MSG_PRIORITY_INTERNALS | MSG_PRIORITY_REEMITTED))
276 : : /* The -fopt-info-internals case. */
277 : 42 : ASSERT_DUMPED_TEXT_EQ
278 : : (tmp,
279 : : "test.c:6:12: note: === function_that_indirectly_fails"
280 : : " ===\n"
281 : : "test.c:6:12: note: === function_that_fails ===\n"
282 : : "test.c:6:12: missed: can't handle return type: int"
283 : : " for stmt: return 0;\n"
284 : : "test.c:5:10: missed: can't optimize loop\n"
285 : : "test.c:6:12: missed: can't handle return type: int"
286 : : " for stmt: return 0;\n");
287 : 126 : else if (effective_filter == MSG_PRIORITY_INTERNALS)
288 : : /* The default for dump files. */
289 : 42 : ASSERT_DUMPED_TEXT_EQ
290 : : (tmp,
291 : : "test.c:6:12: note: === function_that_indirectly_fails"
292 : : " ===\n"
293 : : "test.c:6:12: note: === function_that_fails ===\n"
294 : : "test.c:6:12: missed: can't handle return type: int"
295 : : " for stmt: return 0;\n"
296 : : "test.c:5:10: missed: can't optimize loop\n");
297 : 84 : else if (effective_filter == MSG_PRIORITY_REEMITTED)
298 : : /* The default when -fopt-info is enabled. */
299 : 42 : ASSERT_DUMPED_TEXT_EQ
300 : : (tmp,
301 : : "test.c:5:10: missed: can't optimize loop\n"
302 : : "test.c:6:12: missed: can't handle return type: int"
303 : : " for stmt: return 0;\n");
304 : : else
305 : : {
306 : 42 : gcc_assert (effective_filter == 0);
307 : 42 : ASSERT_DUMPED_TEXT_EQ
308 : : (tmp,
309 : : "test.c:5:10: missed: can't optimize loop\n");
310 : : }
311 : : }
312 : : else
313 : : {
314 : : /* If dumping was disabled, then no problem should have been
315 : : created, and nothing should have been dumped. */
316 : 168 : ASSERT_EQ (problem, NULL);
317 : 168 : ASSERT_DUMPED_TEXT_EQ (tmp, "");
318 : : }
319 : 336 : }
320 : : }
321 : 72 : }
322 : :
323 : : /* Run all of the selftests within this file. */
324 : :
325 : : void
326 : 3 : c_opt_problem_cc_tests ()
327 : : {
328 : 3 : test_opt_result_success ();
329 : 3 : for_each_line_table_case (test_opt_result_failure_at);
330 : 3 : }
331 : :
332 : : } // namespace selftest
333 : :
334 : : #endif /* CHECKING_P */
|