Line data Source code
1 : /* Classes for representing locations within the program.
2 : Copyright (C) 2019-2026 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 "analyzer/common.h"
22 :
23 : #include "diagnostics/event-id.h"
24 : #include "gcc-rich-location.h"
25 : #include "gimple-pretty-print.h"
26 : #include "sbitmap.h"
27 : #include "selftest.h"
28 : #include "shortest-paths.h"
29 :
30 : #include "analyzer/analyzer-logging.h"
31 : #include "analyzer/call-string.h"
32 : #include "analyzer/supergraph.h"
33 : #include "analyzer/program-point.h"
34 : #include "analyzer/store.h"
35 : #include "analyzer/region-model.h"
36 : #include "analyzer/sm.h"
37 : #include "analyzer/program-state.h"
38 : #include "analyzer/pending-diagnostic.h"
39 : #include "analyzer/diagnostic-manager.h"
40 : #include "analyzer/exploded-graph.h"
41 : #include "analyzer/analysis-plan.h"
42 : #include "analyzer/inlining-iterator.h"
43 :
44 : #if ENABLE_ANALYZER
45 :
46 : namespace ana {
47 :
48 : /* A subclass of diagnostics::context for use by
49 : program_point::print_source_line. */
50 :
51 : class debug_diagnostic_context : public diagnostics::context
52 : {
53 : public:
54 639 : debug_diagnostic_context ()
55 639 : {
56 639 : diagnostic_initialize (this, 0);
57 639 : auto &source_printing_opts = get_source_printing_options ();
58 639 : source_printing_opts.show_line_numbers_p = true;
59 639 : source_printing_opts.enabled = true;
60 639 : }
61 639 : ~debug_diagnostic_context ()
62 : {
63 1278 : diagnostic_finish (this);
64 639 : }
65 : };
66 :
67 : /* class program_point. */
68 :
69 : /* Print the source line (if any) for this program_point to PP. */
70 :
71 : void
72 639 : program_point::print_source_line (pretty_printer *pp) const
73 : {
74 1278 : if (!useful_location_p (get_location ()))
75 0 : return;
76 639 : debug_diagnostic_context tmp_dc;
77 1278 : gcc_rich_location richloc (get_location ());
78 639 : diagnostics::source_print_policy source_policy (tmp_dc);
79 639 : gcc_assert (pp);
80 639 : source_policy.print (*pp, richloc, diagnostics::kind::error, nullptr);
81 639 : pp_string (pp, pp_formatted_text (tmp_dc.get_reference_printer ()));
82 1278 : }
83 :
84 : /* Print this program_point to PP. */
85 :
86 : void
87 3577 : program_point::print (pretty_printer *pp, const format &f) const
88 : {
89 3577 : pp_string (pp, "callstring: ");
90 3577 : m_call_string->print (pp);
91 3577 : f.spacer (pp);
92 :
93 3577 : if (m_snode)
94 : {
95 3542 : pp_printf (pp, "sn: %i", m_snode->m_id);
96 3542 : if (f.m_newlines)
97 : {
98 639 : pp_newline (pp);
99 639 : print_source_line (pp);
100 : }
101 : }
102 : else
103 35 : pp_string (pp, "origin");
104 3577 : }
105 :
106 : /* Dump this point to stderr. */
107 :
108 : DEBUG_FUNCTION void
109 0 : program_point::dump () const
110 : {
111 0 : tree_dump_pretty_printer pp (stderr);
112 0 : print (&pp, format (true));
113 0 : }
114 :
115 : /* Return a new json::object of the form
116 : {"snode_idx" : int (optional), the index of the supernode,
117 : "call_string": object for the call_string}. */
118 :
119 : std::unique_ptr<json::object>
120 0 : program_point::to_json () const
121 : {
122 0 : auto point_obj = std::make_unique<json::object> ();
123 :
124 0 : if (get_supernode ())
125 0 : point_obj->set_integer ("snode_idx", get_supernode ()->m_id);
126 0 : point_obj->set ("call_string", m_call_string->to_json ());
127 :
128 0 : return point_obj;
129 : }
130 :
131 : /* Pop the topmost call from the current callstack. */
132 : void
133 1593 : program_point::pop_from_call_stack ()
134 : {
135 1593 : m_call_string = m_call_string->get_parent ();
136 1593 : gcc_assert (m_call_string);
137 1593 : }
138 :
139 : /* Generate a hash value for this program_point. */
140 :
141 : hashval_t
142 4654147 : program_point::hash () const
143 : {
144 4654147 : inchash::hash hstate;
145 4654147 : hstate.add_ptr (m_snode);
146 4654147 : hstate.add_ptr (m_call_string);
147 4654147 : return hstate.end ();
148 : }
149 :
150 : /* Get the function * at DEPTH within the call stack. */
151 :
152 : function *
153 1152259 : program_point::get_function_at_depth (unsigned depth) const
154 : {
155 1724010 : gcc_assert (depth <= m_call_string->length ());
156 1152259 : if (depth == m_call_string->length ())
157 780184 : return m_snode->get_function ();
158 : else
159 372075 : return get_call_string ()[depth].get_caller_function ();
160 : }
161 :
162 : /* Assert that this object is sane. */
163 :
164 : void
165 786934 : program_point::validate () const
166 : {
167 : /* Skip this in a release build. */
168 : #if !CHECKING_P
169 : return;
170 : #endif
171 :
172 786934 : m_call_string->validate ();
173 : /* The "callee" of the final entry in the callstring should be the
174 : function of the m_function_point. */
175 786934 : if (m_call_string->length () > 0)
176 399352 : gcc_assert
177 : ((*m_call_string)[m_call_string->length () - 1].get_callee_function ()
178 : == get_function ());
179 786934 : }
180 :
181 : /* class program_point. */
182 :
183 : program_point
184 3429 : program_point::origin (const region_model_manager &mgr)
185 : {
186 3429 : return program_point (nullptr,
187 3429 : mgr.get_empty_call_string ());
188 : }
189 :
190 : program_point
191 10071 : program_point::from_function_entry (const region_model_manager &mgr,
192 : const supergraph &sg,
193 : const function &fun)
194 : {
195 10071 : return program_point (sg.get_node_for_function_entry (fun),
196 10071 : mgr.get_empty_call_string ());
197 : }
198 :
199 : /* Return true iff POINT_A and POINT_B share the same function and
200 : call_string, both directly, and when attempting to undo inlining
201 : information. */
202 :
203 : bool
204 110 : program_point::effectively_intraprocedural_p (const program_point &point_a,
205 : const program_point &point_b)
206 : {
207 : /* First, compare without considering inlining info. */
208 220 : if (point_a.get_function ()
209 110 : != point_b.get_function ())
210 : return false;
211 110 : if (&point_a.get_call_string ()
212 110 : != &point_b.get_call_string ())
213 : return false;
214 :
215 : /* Consider inlining info; they must have originally come from
216 : the same function and have been inlined in the same way. */
217 110 : location_t loc_a = point_a.get_location ();
218 110 : location_t loc_b = point_b.get_location ();
219 110 : inlining_iterator iter_a (loc_a);
220 110 : inlining_iterator iter_b (loc_b);
221 314 : while (!(iter_a.done_p () || iter_b.done_p ()))
222 : {
223 110 : if (iter_a.done_p () || iter_b.done_p ())
224 : return false;
225 :
226 110 : if (iter_a.get_fndecl () != iter_b.get_fndecl ())
227 : return false;
228 108 : if (iter_a.get_callsite () != iter_b.get_callsite ())
229 : return false;
230 108 : if (iter_a.get_block () != iter_b.get_block ())
231 : return false;
232 :
233 94 : iter_a.next ();
234 94 : iter_b.next ();
235 : }
236 :
237 : return true;
238 : }
239 :
240 : #if CHECKING_P
241 :
242 : namespace selftest {
243 :
244 : /* Verify that program_point::operator== works as expected. */
245 :
246 : static void
247 4 : test_program_point_equality ()
248 : {
249 4 : region_model_manager mgr;
250 :
251 4 : const supernode *snode = nullptr;
252 :
253 4 : const call_string &cs = mgr.get_empty_call_string ();
254 :
255 4 : program_point a = program_point (snode, cs);
256 4 : program_point b = program_point (snode, cs);
257 4 : ASSERT_EQ (a, b);
258 : // TODO: verify with non-empty callstrings, with different snodes
259 4 : }
260 :
261 : /* Run all of the selftests within this file. */
262 :
263 : void
264 4 : analyzer_program_point_cc_tests ()
265 : {
266 4 : test_program_point_equality ();
267 4 : }
268 :
269 : } // namespace selftest
270 :
271 : #endif /* CHECKING_P */
272 :
273 : } // namespace ana
274 :
275 : #endif /* #if ENABLE_ANALYZER */
|