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