mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 20:01:31 -05:00
276 lines
6.8 KiB
C++
276 lines
6.8 KiB
C++
/* Classes for representing locations within the program.
|
|
Copyright (C) 2019-2026 Free Software Foundation, Inc.
|
|
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "analyzer/common.h"
|
|
|
|
#include "diagnostics/event-id.h"
|
|
#include "gcc-rich-location.h"
|
|
#include "gimple-pretty-print.h"
|
|
#include "sbitmap.h"
|
|
#include "selftest.h"
|
|
#include "shortest-paths.h"
|
|
|
|
#include "analyzer/analyzer-logging.h"
|
|
#include "analyzer/call-string.h"
|
|
#include "analyzer/supergraph.h"
|
|
#include "analyzer/program-point.h"
|
|
#include "analyzer/store.h"
|
|
#include "analyzer/region-model.h"
|
|
#include "analyzer/sm.h"
|
|
#include "analyzer/program-state.h"
|
|
#include "analyzer/pending-diagnostic.h"
|
|
#include "analyzer/diagnostic-manager.h"
|
|
#include "analyzer/exploded-graph.h"
|
|
#include "analyzer/analysis-plan.h"
|
|
#include "analyzer/inlining-iterator.h"
|
|
|
|
#if ENABLE_ANALYZER
|
|
|
|
namespace ana {
|
|
|
|
/* A subclass of diagnostics::context for use by
|
|
program_point::print_source_line. */
|
|
|
|
class debug_diagnostic_context : public diagnostics::context
|
|
{
|
|
public:
|
|
debug_diagnostic_context ()
|
|
{
|
|
diagnostic_initialize (this, 0);
|
|
auto &source_printing_opts = get_source_printing_options ();
|
|
source_printing_opts.show_line_numbers_p = true;
|
|
source_printing_opts.enabled = true;
|
|
}
|
|
~debug_diagnostic_context ()
|
|
{
|
|
diagnostic_finish (this);
|
|
}
|
|
};
|
|
|
|
/* class program_point. */
|
|
|
|
/* Print the source line (if any) for this program_point to PP. */
|
|
|
|
void
|
|
program_point::print_source_line (pretty_printer *pp) const
|
|
{
|
|
if (!useful_location_p (get_location ()))
|
|
return;
|
|
debug_diagnostic_context tmp_dc;
|
|
gcc_rich_location richloc (get_location ());
|
|
diagnostics::source_print_policy source_policy (tmp_dc);
|
|
gcc_assert (pp);
|
|
source_policy.print (*pp, richloc, diagnostics::kind::error, nullptr);
|
|
pp_string (pp, pp_formatted_text (tmp_dc.get_reference_printer ()));
|
|
}
|
|
|
|
/* Print this program_point to PP. */
|
|
|
|
void
|
|
program_point::print (pretty_printer *pp, const format &f) const
|
|
{
|
|
pp_string (pp, "callstring: ");
|
|
m_call_string->print (pp);
|
|
f.spacer (pp);
|
|
|
|
if (m_snode)
|
|
{
|
|
pp_printf (pp, "sn: %i", m_snode->m_id);
|
|
if (f.m_newlines)
|
|
{
|
|
pp_newline (pp);
|
|
print_source_line (pp);
|
|
}
|
|
}
|
|
else
|
|
pp_string (pp, "origin");
|
|
}
|
|
|
|
/* Dump this point to stderr. */
|
|
|
|
DEBUG_FUNCTION void
|
|
program_point::dump () const
|
|
{
|
|
tree_dump_pretty_printer pp (stderr);
|
|
print (&pp, format (true));
|
|
}
|
|
|
|
/* Return a new json::object of the form
|
|
{"snode_idx" : int (optional), the index of the supernode,
|
|
"call_string": object for the call_string}. */
|
|
|
|
std::unique_ptr<json::object>
|
|
program_point::to_json () const
|
|
{
|
|
auto point_obj = std::make_unique<json::object> ();
|
|
|
|
if (get_supernode ())
|
|
point_obj->set_integer ("snode_idx", get_supernode ()->m_id);
|
|
point_obj->set ("call_string", m_call_string->to_json ());
|
|
|
|
return point_obj;
|
|
}
|
|
|
|
/* Pop the topmost call from the current callstack. */
|
|
void
|
|
program_point::pop_from_call_stack ()
|
|
{
|
|
m_call_string = m_call_string->get_parent ();
|
|
gcc_assert (m_call_string);
|
|
}
|
|
|
|
/* Generate a hash value for this program_point. */
|
|
|
|
hashval_t
|
|
program_point::hash () const
|
|
{
|
|
inchash::hash hstate;
|
|
hstate.add_ptr (m_snode);
|
|
hstate.add_ptr (m_call_string);
|
|
return hstate.end ();
|
|
}
|
|
|
|
/* Get the function * at DEPTH within the call stack. */
|
|
|
|
function *
|
|
program_point::get_function_at_depth (unsigned depth) const
|
|
{
|
|
gcc_assert (depth <= m_call_string->length ());
|
|
if (depth == m_call_string->length ())
|
|
return m_snode->get_function ();
|
|
else
|
|
return get_call_string ()[depth].get_caller_function ();
|
|
}
|
|
|
|
/* Assert that this object is sane. */
|
|
|
|
void
|
|
program_point::validate () const
|
|
{
|
|
/* Skip this in a release build. */
|
|
#if !CHECKING_P
|
|
return;
|
|
#endif
|
|
|
|
m_call_string->validate ();
|
|
/* The "callee" of the final entry in the callstring should be the
|
|
function of the m_function_point. */
|
|
if (m_call_string->length () > 0)
|
|
gcc_assert
|
|
((*m_call_string)[m_call_string->length () - 1].get_callee_function ()
|
|
== get_function ());
|
|
}
|
|
|
|
/* class program_point. */
|
|
|
|
program_point
|
|
program_point::origin (const region_model_manager &mgr)
|
|
{
|
|
return program_point (nullptr,
|
|
mgr.get_empty_call_string ());
|
|
}
|
|
|
|
program_point
|
|
program_point::from_function_entry (const region_model_manager &mgr,
|
|
const supergraph &sg,
|
|
const function &fun)
|
|
{
|
|
return program_point (sg.get_node_for_function_entry (fun),
|
|
mgr.get_empty_call_string ());
|
|
}
|
|
|
|
/* Return true iff POINT_A and POINT_B share the same function and
|
|
call_string, both directly, and when attempting to undo inlining
|
|
information. */
|
|
|
|
bool
|
|
program_point::effectively_intraprocedural_p (const program_point &point_a,
|
|
const program_point &point_b)
|
|
{
|
|
/* First, compare without considering inlining info. */
|
|
if (point_a.get_function ()
|
|
!= point_b.get_function ())
|
|
return false;
|
|
if (&point_a.get_call_string ()
|
|
!= &point_b.get_call_string ())
|
|
return false;
|
|
|
|
/* Consider inlining info; they must have originally come from
|
|
the same function and have been inlined in the same way. */
|
|
location_t loc_a = point_a.get_location ();
|
|
location_t loc_b = point_b.get_location ();
|
|
inlining_iterator iter_a (loc_a);
|
|
inlining_iterator iter_b (loc_b);
|
|
while (!(iter_a.done_p () || iter_b.done_p ()))
|
|
{
|
|
if (iter_a.done_p () || iter_b.done_p ())
|
|
return false;
|
|
|
|
if (iter_a.get_fndecl () != iter_b.get_fndecl ())
|
|
return false;
|
|
if (iter_a.get_callsite () != iter_b.get_callsite ())
|
|
return false;
|
|
if (iter_a.get_block () != iter_b.get_block ())
|
|
return false;
|
|
|
|
iter_a.next ();
|
|
iter_b.next ();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#if CHECKING_P
|
|
|
|
namespace selftest {
|
|
|
|
/* Verify that program_point::operator== works as expected. */
|
|
|
|
static void
|
|
test_program_point_equality ()
|
|
{
|
|
region_model_manager mgr;
|
|
|
|
const supernode *snode = nullptr;
|
|
|
|
const call_string &cs = mgr.get_empty_call_string ();
|
|
|
|
program_point a = program_point (snode, cs);
|
|
program_point b = program_point (snode, cs);
|
|
ASSERT_EQ (a, b);
|
|
// TODO: verify with non-empty callstrings, with different snodes
|
|
}
|
|
|
|
/* Run all of the selftests within this file. */
|
|
|
|
void
|
|
analyzer_program_point_cc_tests ()
|
|
{
|
|
test_program_point_equality ();
|
|
}
|
|
|
|
} // namespace selftest
|
|
|
|
#endif /* CHECKING_P */
|
|
|
|
} // namespace ana
|
|
|
|
#endif /* #if ENABLE_ANALYZER */
|