1 /* Classes for representing locations within the program. 2 Copyright (C) 2019-2020 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 8 under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3, or (at your option) 10 any later version. 11 12 GCC is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License 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 "tree.h" 25 #include "gimple-pretty-print.h" 26 #include "gcc-rich-location.h" 27 #include "analyzer/call-string.h" 28 #include "ordered-hash-map.h" 29 #include "options.h" 30 #include "cgraph.h" 31 #include "function.h" 32 #include "cfg.h" 33 #include "basic-block.h" 34 #include "gimple.h" 35 #include "gimple-iterator.h" 36 #include "digraph.h" 37 #include "analyzer/analyzer.h" 38 #include "analyzer/analyzer-logging.h" 39 #include "analyzer/supergraph.h" 40 #include "analyzer/program-point.h" 41 #include "sbitmap.h" 42 #include "bitmap.h" 43 #include "tristate.h" 44 #include "selftest.h" 45 #include "analyzer/region-model.h" 46 #include "analyzer/sm.h" 47 #include "analyzer/program-state.h" 48 #include "alloc-pool.h" 49 #include "fibonacci_heap.h" 50 #include "diagnostic-event-id.h" 51 #include "analyzer/pending-diagnostic.h" 52 #include "analyzer/diagnostic-manager.h" 53 #include "shortest-paths.h" 54 #include "analyzer/exploded-graph.h" 55 #include "analyzer/analysis-plan.h" 56 57 #if ENABLE_ANALYZER 58 59 namespace ana { 60 61 /* Get a string for PK. */ 62 63 const char * 64 point_kind_to_string (enum point_kind pk) 65 { 66 switch (pk) 67 { 68 default: 69 gcc_unreachable (); 70 case PK_ORIGIN: 71 return "PK_ORIGIN"; 72 case PK_BEFORE_SUPERNODE: 73 return "PK_BEFORE_SUPERNODE"; 74 case PK_BEFORE_STMT: 75 return "PK_BEFORE_STMT"; 76 case PK_AFTER_SUPERNODE: 77 return "PK_AFTER_SUPERNODE"; 78 case PK_EMPTY: 79 return "PK_EMPTY"; 80 case PK_DELETED: 81 return "PK_DELETED"; 82 } 83 } 84 85 /* class function_point. */ 86 87 /* Print this function_point to PP. */ 88 89 void 90 function_point::print (pretty_printer *pp, const format &f) const 91 { 92 switch (get_kind ()) 93 { 94 default: 95 gcc_unreachable (); 96 97 case PK_ORIGIN: 98 pp_printf (pp, "origin"); 99 break; 100 101 case PK_BEFORE_SUPERNODE: 102 { 103 if (m_from_edge) 104 pp_printf (pp, "before SN: %i (from SN: %i)", 105 m_supernode->m_index, m_from_edge->m_src->m_index); 106 else 107 pp_printf (pp, "before SN: %i (NULL from-edge)", 108 m_supernode->m_index); 109 f.spacer (pp); 110 for (gphi_iterator gpi 111 = const_cast<supernode *>(get_supernode ())->start_phis (); 112 !gsi_end_p (gpi); gsi_next (&gpi)) 113 { 114 const gphi *phi = gpi.phi (); 115 pp_gimple_stmt_1 (pp, phi, 0, (dump_flags_t)0); 116 } 117 } 118 break; 119 120 case PK_BEFORE_STMT: 121 pp_printf (pp, "before (SN: %i stmt: %i): ", m_supernode->m_index, 122 m_stmt_idx); 123 f.spacer (pp); 124 pp_gimple_stmt_1 (pp, get_stmt (), 0, (dump_flags_t)0); 125 if (f.m_newlines) 126 { 127 pp_newline (pp); 128 print_source_line (pp); 129 } 130 break; 131 132 case PK_AFTER_SUPERNODE: 133 pp_printf (pp, "after SN: %i", m_supernode->m_index); 134 break; 135 } 136 } 137 138 /* Generate a hash value for this function_point. */ 139 140 hashval_t 141 function_point::hash () const 142 { 143 inchash::hash hstate; 144 if (m_supernode) 145 hstate.add_int (m_supernode->m_index); 146 hstate.add_ptr (m_from_edge); 147 hstate.add_int (m_stmt_idx); 148 hstate.add_int (m_kind); 149 return hstate.end (); 150 } 151 152 /* Get the gimple stmt for this function_point, if any. */ 153 154 const gimple * 155 function_point::get_stmt () const 156 { 157 if (m_kind == PK_BEFORE_STMT) 158 return m_supernode->m_stmts[m_stmt_idx]; 159 else if (m_kind == PK_AFTER_SUPERNODE) 160 return m_supernode->get_last_stmt (); 161 else 162 return NULL; 163 } 164 165 /* Get a location for this function_point, if any. */ 166 167 location_t 168 function_point::get_location () const 169 { 170 const gimple *stmt = get_stmt (); 171 if (stmt) 172 return stmt->location; 173 174 return UNKNOWN_LOCATION; 175 } 176 177 /* A subclass of diagnostic_context for use by 178 program_point::print_source_line. */ 179 180 class debug_diagnostic_context : public diagnostic_context 181 { 182 public: 183 debug_diagnostic_context () 184 { 185 diagnostic_initialize (this, 0); 186 show_line_numbers_p = true; 187 show_caret = true; 188 } 189 ~debug_diagnostic_context () 190 { 191 diagnostic_finish (this); 192 } 193 }; 194 195 /* Print the source line (if any) for this function_point to PP. */ 196 197 void 198 function_point::print_source_line (pretty_printer *pp) const 199 { 200 const gimple *stmt = get_stmt (); 201 if (!stmt) 202 return; 203 // TODO: monospace font 204 debug_diagnostic_context tmp_dc; 205 gcc_rich_location richloc (stmt->location); 206 diagnostic_show_locus (&tmp_dc, &richloc, DK_ERROR); 207 pp_string (pp, pp_formatted_text (tmp_dc.printer)); 208 } 209 210 /* class program_point. */ 211 212 /* Print this program_point to PP. */ 213 214 void 215 program_point::print (pretty_printer *pp, const format &f) const 216 { 217 pp_string (pp, "callstring: "); 218 m_call_string.print (pp); 219 f.spacer (pp); 220 221 m_function_point.print (pp, f); 222 } 223 224 /* Dump this point to stderr. */ 225 226 DEBUG_FUNCTION void 227 program_point::dump () const 228 { 229 pretty_printer pp; 230 pp_show_color (&pp) = pp_show_color (global_dc->printer); 231 pp.buffer->stream = stderr; 232 print (&pp, format (true)); 233 pp_flush (&pp); 234 } 235 236 /* Generate a hash value for this program_point. */ 237 238 hashval_t 239 program_point::hash () const 240 { 241 inchash::hash hstate; 242 hstate.merge_hash (m_function_point.hash ()); 243 hstate.merge_hash (m_call_string.hash ()); 244 return hstate.end (); 245 } 246 247 /* Get the function * at DEPTH within the call stack. */ 248 249 function * 250 program_point::get_function_at_depth (unsigned depth) const 251 { 252 gcc_assert (depth <= m_call_string.length ()); 253 if (depth == m_call_string.length ()) 254 return m_function_point.get_function (); 255 else 256 return m_call_string[depth]->get_caller_function (); 257 } 258 259 /* Assert that this object is sane. */ 260 261 void 262 program_point::validate () const 263 { 264 /* Skip this in a release build. */ 265 #if !CHECKING_P 266 return; 267 #endif 268 269 m_call_string.validate (); 270 /* The "callee" of the final entry in the callstring should be the 271 function of the m_function_point. */ 272 if (m_call_string.length () > 0) 273 gcc_assert (m_call_string[m_call_string.length () - 1]->get_callee_function () 274 == get_function ()); 275 } 276 277 /* Check to see if SUCC is a valid edge to take (ensuring that we have 278 interprocedurally valid paths in the exploded graph, and enforcing 279 recursion limits). 280 281 Update the call string if SUCC is a call or a return. 282 283 Return true if SUCC can be taken, or false otherwise. 284 285 This is the "point" half of exploded_node::on_edge. */ 286 287 bool 288 program_point::on_edge (exploded_graph &eg, 289 const superedge *succ) 290 { 291 logger * const logger = eg.get_logger (); 292 LOG_FUNC (logger); 293 switch (succ->m_kind) 294 { 295 case SUPEREDGE_CFG_EDGE: 296 { 297 const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (succ); 298 299 /* Reject abnormal edges; we special-case setjmp/longjmp. */ 300 if (cfg_sedge->get_flags () & EDGE_ABNORMAL) 301 return false; 302 } 303 break; 304 305 case SUPEREDGE_CALL: 306 { 307 const call_superedge *call_sedge = as_a <const call_superedge *> (succ); 308 309 if (eg.get_analysis_plan ().use_summary_p (call_sedge->m_cedge)) 310 { 311 if (logger) 312 logger->log ("rejecting call edge: using summary instead"); 313 return false; 314 } 315 316 /* Add the callsite to the call string. */ 317 m_call_string.push_call (eg.get_supergraph (), call_sedge); 318 319 /* Impose a maximum recursion depth and don't analyze paths 320 that exceed it further. 321 This is something of a blunt workaround, but it only 322 applies to recursion (and mutual recursion), not to 323 general call stacks. */ 324 if (m_call_string.calc_recursion_depth () 325 > param_analyzer_max_recursion_depth) 326 { 327 if (logger) 328 logger->log ("rejecting call edge: recursion limit exceeded"); 329 // TODO: issue a sorry for this? 330 return false; 331 } 332 } 333 break; 334 335 case SUPEREDGE_RETURN: 336 { 337 /* Require that we return to the call site in the call string. */ 338 if (m_call_string.empty_p ()) 339 { 340 if (logger) 341 logger->log ("rejecting return edge: empty call string"); 342 return false; 343 } 344 const return_superedge *top_of_stack = m_call_string.pop (); 345 if (top_of_stack != succ) 346 { 347 if (logger) 348 logger->log ("rejecting return edge: return to wrong callsite"); 349 return false; 350 } 351 } 352 break; 353 354 case SUPEREDGE_INTRAPROCEDURAL_CALL: 355 { 356 const callgraph_superedge *cg_sedge 357 = as_a <const callgraph_superedge *> (succ); 358 /* Consider turning this edge into a use of an 359 interprocedural summary. */ 360 if (eg.get_analysis_plan ().use_summary_p (cg_sedge->m_cedge)) 361 { 362 if (logger) 363 logger->log ("using function summary for %qE in %qE", 364 cg_sedge->get_callee_decl (), 365 cg_sedge->get_caller_decl ()); 366 return true; 367 } 368 else 369 { 370 /* Otherwise, we ignore these edges */ 371 if (logger) 372 logger->log ("rejecting interprocedural edge"); 373 return false; 374 } 375 } 376 } 377 378 return true; 379 } 380 381 /* Comparator for program points within the same supernode, 382 for implementing worklist::key_t comparison operators. 383 Return negative if POINT_A is before POINT_B 384 Return positive if POINT_A is after POINT_B 385 Return 0 if they are equal. */ 386 387 int 388 function_point::cmp_within_supernode_1 (const function_point &point_a, 389 const function_point &point_b) 390 { 391 gcc_assert (point_a.get_supernode () == point_b.get_supernode ()); 392 393 switch (point_a.m_kind) 394 { 395 default: 396 gcc_unreachable (); 397 case PK_BEFORE_SUPERNODE: 398 switch (point_b.m_kind) 399 { 400 default: 401 gcc_unreachable (); 402 case PK_BEFORE_SUPERNODE: 403 { 404 int a_src_idx = -1; 405 int b_src_idx = -1; 406 if (point_a.m_from_edge) 407 a_src_idx = point_a.m_from_edge->m_src->m_index; 408 if (point_b.m_from_edge) 409 b_src_idx = point_b.m_from_edge->m_src->m_index; 410 return a_src_idx - b_src_idx; 411 } 412 break; 413 414 case PK_BEFORE_STMT: 415 case PK_AFTER_SUPERNODE: 416 return -1; 417 } 418 break; 419 case PK_BEFORE_STMT: 420 switch (point_b.m_kind) 421 { 422 default: 423 gcc_unreachable (); 424 case PK_BEFORE_SUPERNODE: 425 return 1; 426 427 case PK_BEFORE_STMT: 428 return point_a.m_stmt_idx - point_b.m_stmt_idx; 429 430 case PK_AFTER_SUPERNODE: 431 return -1; 432 } 433 break; 434 case PK_AFTER_SUPERNODE: 435 switch (point_b.m_kind) 436 { 437 default: 438 gcc_unreachable (); 439 case PK_BEFORE_SUPERNODE: 440 case PK_BEFORE_STMT: 441 return 1; 442 443 case PK_AFTER_SUPERNODE: 444 return 0; 445 } 446 break; 447 } 448 } 449 450 /* Comparator for program points within the same supernode, 451 for implementing worklist::key_t comparison operators. 452 Return negative if POINT_A is before POINT_B 453 Return positive if POINT_A is after POINT_B 454 Return 0 if they are equal. */ 455 456 int 457 function_point::cmp_within_supernode (const function_point &point_a, 458 const function_point &point_b) 459 { 460 int result = cmp_within_supernode_1 (point_a, point_b); 461 462 /* Check that the ordering is symmetric */ 463 #if CHECKING_P 464 int reversed = cmp_within_supernode_1 (point_b, point_a); 465 gcc_assert (reversed == -result); 466 #endif 467 468 return result; 469 } 470 471 #if CHECKING_P 472 473 namespace selftest { 474 475 /* Verify that function_point::operator== works as expected. */ 476 477 static void 478 test_function_point_equality () 479 { 480 const supernode *snode = NULL; 481 482 function_point a = function_point (snode, NULL, 0, 483 PK_BEFORE_SUPERNODE); 484 function_point b = function_point::before_supernode (snode, NULL); 485 ASSERT_EQ (a, b); 486 } 487 488 /* Verify that function_point::cmp_within_supernode works as expected. */ 489 490 static void 491 test_function_point_ordering () 492 { 493 const supernode *snode = NULL; 494 const call_string call_string; 495 496 /* Populate an array with various points within the same 497 snode, in order. */ 498 auto_vec<function_point> points; 499 points.safe_push (function_point::before_supernode (snode, NULL)); 500 points.safe_push (function_point::before_stmt (snode, 0)); 501 points.safe_push (function_point::before_stmt (snode, 1)); 502 points.safe_push (function_point::after_supernode (snode)); 503 504 /* Check all pairs. */ 505 unsigned i; 506 function_point *point_a; 507 FOR_EACH_VEC_ELT (points, i, point_a) 508 { 509 unsigned j; 510 function_point *point_b; 511 FOR_EACH_VEC_ELT (points, j, point_b) 512 { 513 int cmp = function_point::cmp_within_supernode (*point_a, *point_b); 514 if (i == j) 515 ASSERT_EQ (cmp, 0); 516 if (i < j) 517 ASSERT_TRUE (cmp < 0); 518 if (i > j) 519 ASSERT_TRUE (cmp > 0); 520 } 521 } 522 } 523 524 /* Verify that program_point::operator== works as expected. */ 525 526 static void 527 test_program_point_equality () 528 { 529 const supernode *snode = NULL; 530 531 const call_string cs; 532 533 program_point a = program_point::before_supernode (snode, NULL, 534 cs); 535 536 program_point b = program_point::before_supernode (snode, NULL, 537 cs); 538 539 ASSERT_EQ (a, b); 540 // TODO: verify with non-empty callstrings, with different edges 541 } 542 543 /* Run all of the selftests within this file. */ 544 545 void 546 analyzer_program_point_cc_tests () 547 { 548 test_function_point_equality (); 549 test_function_point_ordering (); 550 test_program_point_equality (); 551 } 552 553 } // namespace selftest 554 555 #endif /* CHECKING_P */ 556 557 } // namespace ana 558 559 #endif /* #if ENABLE_ANALYZER */ 560