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

source code of gcc/opt-problem.cc