Line data Source code
1 : /* rtegraph.cc graph and nodes used by m2rte.
2 :
3 : Copyright (C) 2019-2026 Free Software Foundation, Inc.
4 : Contributed by Gaius Mulley <gaius@glam.ac.uk>.
5 :
6 : This file is part of GNU Modula-2.
7 :
8 : GNU Modula-2 is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3, or (at your option)
11 : any later version.
12 :
13 : GNU Modula-2 is distributed in the hope that it will be useful, but
14 : WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with GNU Modula-2; see the file COPYING3. If not see
20 : <http://www.gnu.org/licenses/>. */
21 :
22 : #include "gcc-consolidation.h"
23 :
24 : #include "../gm2-lang.h"
25 : #include "../m2-tree.h"
26 :
27 : #include "langhooks-def.h" /* FIXME: for lhd_set_decl_assembler_name. */
28 : #include "tree-pass.h" /* FIXME: only for PROP_gimple_any. */
29 : #include "toplev.h"
30 : #include "debug.h"
31 :
32 : #include "opts.h"
33 : #include "mpfr.h"
34 :
35 : #undef DEBUGGING
36 :
37 : struct GTY (()) rtenode
38 : {
39 : bool constructor_reachable; /* Is this guarenteed to be reachable by a constructor? */
40 : bool export_reachable; /* Is this reachable via exported functions? */
41 : bool exception_routine; /* Is this an exception routine? */
42 : bool constructor_final; /* Have we walked this rtenode during constructor testing? */
43 : bool export_final; /* Walked this rtenode during exported testing? */
44 : bool is_call; /* Is this a function call? */
45 : gimple *grtenode;
46 : tree func;
47 : rtenode *reachable_src; /* If this is reachable which src function will call us? */
48 :
49 : vec<rtenode *, va_gc> *function_call;
50 : vec<rtenode *, va_gc> *rts_call;
51 : void dump (void);
52 : void dump_vec (const char *title, vec<rtenode *, va_gc> *list);
53 :
54 : void propagate_constructor_reachable (rtenode *);
55 : void propagate_export_reachable (rtenode *);
56 : void error_message (void);
57 : void warning_message (void);
58 : void note_message (void);
59 : const char *get_func_name (void);
60 : const char *create_message (const char *with_name, const char *without_name);
61 : };
62 :
63 :
64 : typedef vec<rtenode *, va_gc> rtevec;
65 :
66 : static GTY (()) rtevec *allnodes;
67 : static GTY (()) rtevec *candidates;
68 : static GTY (()) rtevec *externs;
69 : static GTY (()) rtevec *constructors;
70 :
71 :
72 : static void determine_reachable (void);
73 : static void issue_messages (void);
74 : void rtegraph_dump (void);
75 :
76 :
77 : static GTY (()) rtenode *rtegraph_current_function = NULL;
78 :
79 :
80 : /* rtegraph_get_func returns the function associated with the rtenode. */
81 :
82 : tree
83 433 : rtegraph_get_func (rtenode *n)
84 : {
85 433 : return n->func;
86 : }
87 :
88 : /* rtegraph_set_current_function assigns rtegraph_current_function with func. */
89 :
90 : void
91 433 : rtegraph_set_current_function (rtenode *func)
92 : {
93 433 : rtegraph_current_function = func;
94 433 : }
95 :
96 : /* rtegraph_include_rtscall mark func as an exception routine and remember
97 : that it is called from rtegraph_current_function in the rts_call array. */
98 :
99 103 : void rtegraph_include_rtscall (rtenode *func)
100 : {
101 : /* This is a runtime exception, mark it as such. */
102 103 : func->exception_routine = true;
103 : /* And remember it. */
104 103 : vec_safe_push (rtegraph_current_function->rts_call, func);
105 103 : }
106 :
107 :
108 : /* rtegraph_include_rtscall remember that rtegraph_current_function calls
109 : func. */
110 :
111 233 : void rtegraph_include_function_call (rtenode *func)
112 : {
113 233 : vec_safe_push (rtegraph_current_function->function_call, func);
114 233 : }
115 :
116 :
117 : /* rtegraph_discover performs the main work, called by m2rte.cc analyse_graph.
118 : It determines which function calls a reachable and then issues any warning
119 : message if a reachable function is a call to a runtime exception handler. */
120 :
121 103 : void rtegraph_discover (void)
122 : {
123 103 : determine_reachable ();
124 : #if defined (DEBUGGING)
125 : rtegraph_dump ();
126 : #endif
127 103 : issue_messages ();
128 103 : }
129 :
130 : /* rtegraph_candidates_include include node n in the array of candidates. */
131 :
132 103 : void rtegraph_candidates_include (rtenode *n)
133 : {
134 103 : unsigned int len = vec_safe_length (candidates);
135 :
136 103 : for (unsigned int i = 0; i < len; i++)
137 0 : if ((*candidates)[i] == n)
138 : return;
139 103 : vec_safe_push (candidates, n);
140 : }
141 :
142 : /* rtegraph_allnodes_include include node n in the array of allnodes. */
143 :
144 0 : void rtegraph_allnodes_include (rtenode *n)
145 : {
146 0 : unsigned int len = vec_safe_length (allnodes);
147 :
148 0 : for (unsigned int i = 0; i < len; i++)
149 0 : if ((*allnodes)[i] == n)
150 : return;
151 0 : vec_safe_push (allnodes, n);
152 : }
153 :
154 : /* rtegraph_externs_include include node n in the array of externs. */
155 :
156 330 : void rtegraph_externs_include (rtenode *n)
157 : {
158 330 : unsigned int len = vec_safe_length (externs);
159 :
160 702 : for (unsigned int i = 0; i < len; i++)
161 372 : if ((*externs)[i] == n)
162 : return;
163 330 : vec_safe_push (externs, n);
164 : }
165 :
166 : /* rtegraph_constructors_include include node n in the array of constructors. */
167 :
168 103 : void rtegraph_constructors_include (rtenode *n)
169 : {
170 103 : unsigned int len = vec_safe_length (constructors);
171 :
172 103 : for (unsigned int i = 0; i < len; i++)
173 0 : if ((*constructors)[i] == n)
174 : return;
175 103 : vec_safe_push (constructors, n);
176 : }
177 :
178 : /* determine_reachable mark modules constructors as reachable and
179 : also mark the exported functions as also reachable. */
180 :
181 103 : void determine_reachable (void)
182 : {
183 103 : unsigned int len = vec_safe_length (constructors);
184 206 : for (unsigned int i = 0; i < len; i++)
185 103 : (*constructors)[i]->propagate_constructor_reachable ((*constructors)[i]);
186 103 : len = vec_safe_length (externs);
187 433 : for (unsigned int i = 0; i < len; i++)
188 330 : (*externs)[i]->propagate_export_reachable ((*externs)[i]);
189 103 : }
190 :
191 : /* issue_messages for every candidate which is constructor reachable issue
192 : an error. For each candidate which is reachable via an external call
193 : issue a warning, for any other candidate (of a local procedure) issue
194 : a note. */
195 :
196 103 : void issue_messages (void)
197 : {
198 103 : unsigned int len = vec_safe_length (candidates);
199 206 : for (unsigned int i = 0; i < len; i++)
200 : {
201 103 : if ((*candidates)[i]->constructor_reachable)
202 83 : (*candidates)[i]->error_message ();
203 20 : else if ((*candidates)[i]->export_reachable)
204 20 : (*candidates)[i]->warning_message ();
205 : else
206 0 : (*candidates)[i]->note_message ();
207 : }
208 103 : }
209 :
210 :
211 : #if defined (DEBUGGING)
212 : /* rtegraph_dump_vec display the contents of a vector array. */
213 :
214 : void
215 : rtegraph_dump_vec (const char *title, vec<rtenode *, va_gc> *list)
216 : {
217 : unsigned int len = vec_safe_length (list);
218 : printf ("%s (length = %d)\n", title, len);
219 : for (unsigned int i = 0; i < len; i++)
220 : {
221 : printf ("[%d]: rtenode %p ", i, (*list)[i]);
222 : (*list)[i]->dump ();
223 : }
224 : printf ("end\n");
225 : }
226 :
227 : /* rtegraph_dump display the contents of each vector array. */
228 :
229 : void rtegraph_dump (void)
230 : {
231 : rtegraph_dump_vec ("allnodes", allnodes);
232 : rtegraph_dump_vec ("candidates", candidates);
233 : rtegraph_dump_vec ("externs", externs);
234 : rtegraph_dump_vec ("constructors", constructors);
235 : }
236 : #endif
237 :
238 : /* rtegraph_init_rtenode create and return a new rtenode. */
239 :
240 : rtenode *
241 769 : rtegraph_init_rtenode (gimple *g, tree fndecl, bool is_func_call)
242 : {
243 769 : rtenode *n = ggc_alloc<rtenode> ();
244 :
245 769 : n->constructor_reachable = false;
246 769 : n->export_reachable = false;
247 769 : n->constructor_final = false;
248 769 : n->export_final = false;
249 769 : n->is_call = is_func_call;
250 769 : n->grtenode = g;
251 769 : n->func = fndecl;
252 769 : n->reachable_src = NULL;
253 :
254 769 : vec_alloc (n->function_call, 0);
255 : // n->function_call = ggc_alloc<rtevec> ();
256 769 : gcc_assert (vec_safe_length (n->function_call) == 0);
257 769 : vec_alloc (n->rts_call, 0);
258 : // n->rts_call = ggc_alloc<rtevec> ();
259 769 : gcc_assert (vec_safe_length (n->rts_call) == 0);
260 769 : return n;
261 : }
262 :
263 : /* rtegraph_lookup attempts to lookup a rtenode associated with a fndecl
264 : which is a function call from node g. */
265 :
266 : rtenode *
267 769 : rtegraph_lookup (gimple *g, tree fndecl, bool is_call)
268 : {
269 769 : unsigned int len = vec_safe_length (allnodes);
270 3273 : for (unsigned int i = 0; i < len; i++)
271 2504 : if ((*allnodes)[i]->grtenode == g
272 702 : && (*allnodes)[i]->func == fndecl
273 2504 : && (*allnodes)[i]->is_call == is_call)
274 : return (*allnodes)[i];
275 769 : rtenode *n = rtegraph_init_rtenode (g, fndecl, is_call);
276 769 : vec_safe_push (allnodes, n);
277 : #if defined (DEBUGGING)
278 : rtegraph_dump ();
279 : #endif
280 769 : return n;
281 : }
282 :
283 : /* rte_error_at - wraps up an error message. */
284 :
285 : static void
286 103 : rte_error_at (location_t location, diagnostics::kind kind,
287 : const char *message, ...)
288 : {
289 103 : diagnostics::diagnostic_info diagnostic;
290 103 : va_list ap;
291 103 : rich_location richloc (line_table, location);
292 :
293 103 : va_start (ap, message);
294 103 : diagnostic_set_info (&diagnostic, message, &ap, &richloc, kind);
295 103 : diagnostic_report_diagnostic (global_dc, &diagnostic);
296 103 : va_end (ap);
297 103 : }
298 :
299 : /* access_int return true if the tree t contains a constant integer, if so then
300 : its value is assigned to *value. */
301 :
302 : static bool
303 206 : access_int (tree t, int *value)
304 : {
305 206 : enum tree_code code = TREE_CODE (t);
306 :
307 206 : if (code == SSA_NAME)
308 0 : return access_int (SSA_NAME_VAR (t), value);
309 206 : if (code == INTEGER_CST)
310 : {
311 206 : *value = TREE_INT_CST_LOW (t);
312 206 : return true;
313 : }
314 0 : if ((code == VAR_DECL || code == PARM_DECL)
315 0 : && DECL_HAS_VALUE_EXPR_P (t))
316 0 : return access_int (DECL_VALUE_EXPR (t), value);
317 : return false;
318 : }
319 :
320 : /* access_string return true if the tree t contains a constant string, if so then
321 : its value is assigned to *value. */
322 :
323 : static bool
324 309 : access_string (tree t, const char **value)
325 : {
326 309 : if (TREE_CODE (t) == ADDR_EXPR)
327 : {
328 309 : if (TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST)
329 : {
330 309 : *value = TREE_STRING_POINTER (TREE_OPERAND (t, 0));
331 309 : return true;
332 : }
333 : }
334 : return false;
335 : }
336 :
337 : /* generate an error using the parameters of the M2RTS exception handler to
338 : locate the source code. We dont use location, as the error_at function will
339 : give the function context which might be misleading if this is inlined. */
340 :
341 : static void
342 103 : generate_report (gimple *stmt, const char *report, diagnostics::kind kind)
343 : {
344 103 : if (gimple_call_num_args (stmt) == 5)
345 : {
346 103 : tree s0 = gimple_call_arg (stmt, 0);
347 103 : tree i1 = gimple_call_arg (stmt, 1);
348 103 : tree i2 = gimple_call_arg (stmt, 2);
349 103 : tree s1 = gimple_call_arg (stmt, 3);
350 103 : tree s2 = gimple_call_arg (stmt, 4);
351 103 : const char *file;
352 103 : int line;
353 103 : int col;
354 103 : const char *scope;
355 103 : const char *message;
356 :
357 103 : if (access_string (s0, &file)
358 103 : && access_int (i1, &line)
359 103 : && access_int (i2, &col)
360 103 : && access_string (s1, &scope)
361 206 : && access_string (s2, &message))
362 : {
363 : /* Continue to use scope as this will survive any
364 : optimization transforms. */
365 103 : location_t location = gimple_location (stmt);
366 103 : rte_error_at (location, kind, "In %s\n%s, %s",
367 : scope, report, message);
368 : }
369 : }
370 103 : }
371 :
372 : /* get_func_name returns the name of the function associated with rtenode. */
373 :
374 20 : const char *rtenode::get_func_name (void)
375 : {
376 20 : if (func != NULL && (DECL_NAME (func) != NULL))
377 20 : return IDENTIFIER_POINTER (DECL_NAME (func));
378 : return NULL;
379 : }
380 :
381 : /* create_message if the current rtenode has a named function associated with it then
382 : create a new message using with_name and the function name, otherwise
383 : return without_name. */
384 :
385 20 : const char *rtenode::create_message (const char *with_name, const char *without_name)
386 : {
387 20 : const char *name = get_func_name ();
388 20 : if (name == NULL)
389 : return without_name;
390 :
391 20 : int len = strlen (with_name) + 1 + strlen (name);
392 20 : char *message = XNEWVEC (char, len);
393 20 : snprintf (message, len, with_name, name);
394 20 : return message;
395 : }
396 :
397 : /* error_message issue an diagnostics::kind::error from grtenode. */
398 :
399 83 : void rtenode::error_message (void)
400 : {
401 83 : if (grtenode != NULL)
402 83 : generate_report (grtenode, "runtime error will occur",
403 : diagnostics::kind::error);
404 83 : }
405 :
406 : /* warning_message issue an diagnostics::kind::warning from grtenode. */
407 :
408 20 : void rtenode::warning_message (void)
409 : {
410 20 : const char *message = reachable_src->create_message
411 20 : ("runtime error will occur if an exported procedure is called from %s",
412 : "runtime error will occur if an exported procedure is called");
413 20 : if (grtenode != NULL)
414 20 : generate_report (grtenode, message, diagnostics::kind::warning);
415 20 : }
416 :
417 : /* note_message issue an diagnostics::kind::note from grtenode. */
418 :
419 0 : void rtenode::note_message (void)
420 : {
421 0 : if (grtenode != NULL)
422 0 : generate_report (grtenode, "runtime will occur if this procedure is called",
423 : diagnostics::kind::note);
424 0 : }
425 :
426 : /* dump_vec display contents of vector array list. */
427 : #if defined (DEBUGGING)
428 : void
429 : rtenode::dump_vec (const char *title, vec<rtenode *, va_gc> *list)
430 : {
431 : printf (" %s (length = %d)\n", title, vec_safe_length (list));
432 : for (unsigned int i = 0; i < vec_safe_length (list); i++)
433 : printf (" [%d]: rtenode %p\n", i, (*list)[i]);
434 : }
435 : #endif
436 :
437 : /* dump display all vector arrays associated with rtenode. */
438 :
439 : void
440 0 : rtenode::dump (void)
441 : {
442 : #if defined (DEBUGGING)
443 : printf ("rtenode::dump:");
444 : if (func != NULL && (DECL_NAME (func) != NULL))
445 : {
446 : const char *n = IDENTIFIER_POINTER (DECL_NAME (func));
447 : printf ("%s", n);
448 : }
449 : if (constructor_reachable)
450 : printf (", constructor_reachable");
451 : if (export_reachable)
452 : printf (", export_reachable");
453 : if (constructor_final)
454 : printf (", constructor_final");
455 : if (export_final)
456 : printf (", export_final");
457 : if (is_call)
458 : printf (", is_call");
459 : else
460 : printf (", decl");
461 : printf (", grtenode %p, func = %p\n", grtenode, func);
462 : dump_vec ("function_call", function_call);
463 : dump_vec ("rts_call", rts_call);
464 : #endif
465 0 : }
466 :
467 : /* propagate_constructor_reachable for every function which is reachable from
468 : rtenode call the callee rtenode and mark it as reachable from a
469 : constructor. */
470 :
471 190 : void rtenode::propagate_constructor_reachable (rtenode *src)
472 : {
473 190 : if (constructor_final)
474 : return;
475 190 : constructor_final = true;
476 190 : constructor_reachable = true;
477 190 : reachable_src = src;
478 202 : for (unsigned int i = 0; i < vec_safe_length (function_call); i++)
479 4 : (*function_call)[i]->propagate_constructor_reachable (src);
480 273 : for (unsigned int i = 0; i < vec_safe_length (rts_call); i++)
481 83 : (*rts_call)[i]->propagate_constructor_reachable (src);
482 : }
483 :
484 : /* propagate_export_reachable for every function which is reachable
485 : from rtenode call the callee rtenode and mark it as reachable from
486 : an exported function. */
487 :
488 541 : void rtenode::propagate_export_reachable (rtenode *src)
489 : {
490 541 : if (export_final)
491 : return;
492 541 : export_final = true;
493 541 : export_reachable = true;
494 541 : reachable_src = src;
495 865 : for (unsigned int i = 0; i < vec_safe_length (function_call); i++)
496 108 : (*function_call)[i]->propagate_export_reachable (src);
497 644 : for (unsigned int i = 0; i < vec_safe_length (rts_call); i++)
498 103 : (*rts_call)[i]->propagate_export_reachable (src);
499 : }
500 :
501 : /* rtegraph_init initialize the data structures (vec arrays) in this
502 : file. */
503 :
504 107 : void rtegraph_init (void)
505 : {
506 107 : vec_alloc (allnodes, 0);
507 107 : gcc_assert (vec_safe_length (allnodes) == 0);
508 107 : vec_alloc (candidates, 0);
509 107 : gcc_assert (vec_safe_length (candidates) == 0);
510 107 : vec_alloc (externs, 0);
511 107 : gcc_assert (vec_safe_length (externs) == 0);
512 107 : vec_alloc (constructors, 0);
513 107 : gcc_assert (vec_safe_length (constructors) == 0);
514 : #if defined (DEBUGGING)
515 : rtegraph_dump ();
516 : #endif
517 107 : }
518 :
519 : /* rtegraph_finish deallocate all vec arrays in this file. */
520 :
521 103 : void rtegraph_finish (void)
522 : {
523 103 : rtegraph_current_function = NULL;
524 103 : vec_free (allnodes);
525 103 : vec_free (candidates);
526 103 : vec_free (externs);
527 103 : vec_free (constructors);
528 103 : }
529 :
530 : #include "gt-m2-rtegraph.h"
|