aboutsummaryrefslogtreecommitdiff
path: root/gcc/m2/plugin/m2rte.cc
blob: 343384ac23173ec36d2e175c19b306c05cfafd05 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
/* m2rte.cc a plugin to detect runtime exceptions at compiletime.

Copyright (C) 2017-2023 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius@glam.ac.uk>.

This file is part of GNU Modula-2.

GNU Modula-2 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.

GNU Modula-2 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 GNU Modula-2; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */


#include "gcc-plugin.h"
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "options.h"
#include "tree-pass.h"
#include "diagnostic-core.h"
#include "flags.h"
#include "intl.h"
#include "plugin.h"
#include "tree.h"
#include "gimple.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "gimplify-me.h"
#include "gimple-pretty-print.h"
#include "plugin-version.h"
#include "diagnostic.h"
#include "context.h"

#include "rtegraph.h"
extern bool ggc_force_collect;
extern void ggc_collect (void);

#undef DEBUG_BASICBLOCK

int plugin_is_GPL_compatible;

void debug_tree (tree);

/* All dialects of Modula-2 issue some or all of these runtime error calls.
   This plugin detects whether a runtime error will be called in the first
   basic block of a reachable function.  */

static const char *m2_runtime_error_calls[] = {
  "M2RTS_AssignmentException",
  "M2RTS_ReturnException",
  "M2RTS_IncException",
  "M2RTS_DecException",
  "M2RTS_InclException",
  "M2RTS_ExclException",
  "M2RTS_ShiftException",
  "M2RTS_RotateException",
  "M2RTS_StaticArraySubscriptException",
  "M2RTS_DynamicArraySubscriptException",
  "M2RTS_ForLoopBeginException",
  "M2RTS_ForLoopToException",
  "M2RTS_ForLoopEndException",
  "M2RTS_PointerNilException",
  "M2RTS_NoReturnException",
  "M2RTS_CaseException",
  "M2RTS_WholeNonPosDivException",
  "M2RTS_WholeNonPosModException",
  "M2RTS_WholeZeroDivException",
  "M2RTS_WholeZeroRemException",
  "M2RTS_WholeValueException",
  "M2RTS_RealValueException",
  "M2RTS_ParameterException",
  "M2RTS_NoException",
  NULL,
};


#if defined(DEBUG_BASICBLOCK)
/* pretty_function display the name of the function.  */

static void
pretty_function (tree fndecl)
{
  if (fndecl != NULL && (DECL_NAME (fndecl) != NULL))
    {
      const char *n = IDENTIFIER_POINTER (DECL_NAME (fndecl));
      fprintf (stderr, "PROCEDURE %s ;\n", n);
    }
}
#endif

void
print_rtl (FILE *outf, const_rtx rtx_first);

/* strend returns true if string name has ending.  */

static bool
strend (const char *name, const char *ending)
{
  unsigned int len = strlen (name);
  return (len > strlen (ending)
	  && (strcmp (&name[len-strlen (ending)], ending) == 0));
}

/* is_constructor returns true if the function name is that of a module
   constructor or deconstructor.  */

static bool
is_constructor (tree fndecl)
{
  const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
  unsigned int len = strlen (name);

  return ((len > strlen ("_M2_"))
	  && (strncmp (name, "_M2_", strlen ("_M2_")) == 0)
	  && (strend (name, "_init") || strend (name, "_finish")));
}

/* is_external returns true if the function is extern.  */

static bool
is_external (tree function)
{
  return (! DECL_EXTERNAL (function))
    && TREE_PUBLIC (function)
    && TREE_STATIC (function);
}

/* is_external returns true if the function is a call to a Modula-2
   runtime exception handler.  */

static bool
is_rte (tree fndecl)
{
  const char *n = IDENTIFIER_POINTER (DECL_NAME (fndecl));

  for (int i = 0; m2_runtime_error_calls[i] != NULL; i++)
    if (strcmp (m2_runtime_error_calls[i], n) == 0)
      return true;
  return false;
}

/* examine_call extract the function tree from the gimple call
   statement and check whether it is a call to a runtime exception.  */

static void
examine_call (gimple *stmt)
{
  tree fndecl = gimple_call_fndecl (stmt);
  rtenode *func = rtegraph_lookup (stmt, fndecl, true);
  // rtegraph_dump ();
  if (fndecl != NULL && (DECL_NAME (fndecl) != NULL))
    {
      /* Firstly check if the function is a runtime exception.  */
      if (is_rte (fndecl))
	{
	  /* Remember runtime exception call.  */
	  rtegraph_include_rtscall (func);
	  /* Add the callee to the list of candidates to be queried reachable.  */
	  rtegraph_candidates_include (func);
	  return;
	}
    }
  /* Add it to the list of calls.  */
  rtegraph_include_function_call (func);
}


/* examine_function_decl, check if the current function is a module
   constructor/deconstructor.  Also check if the current function is
   declared as external.  */

static void
examine_function_decl (rtenode *rt)
{
  tree fndecl = rtegraph_get_func (rt);
  if (fndecl != NULL && (DECL_NAME (fndecl) != NULL))
    {
      /* Check if the function is a module constructor.  */
      if (is_constructor (fndecl))
	rtegraph_constructors_include (rt);
      /* Can it be called externally?  */
      if (is_external (fndecl))
	rtegraph_externs_include (rt);
    }
}


/* Check and warn if STMT is a self-assign statement.  */

static void
runtime_exception_inevitable (gimple *stmt)
{
  if (is_gimple_call (stmt))
    examine_call (stmt);
}


namespace {

const pass_data pass_data_exception_detection =
{
  GIMPLE_PASS, /* type */
  "runtime_exception_inevitable", /* name */
  OPTGROUP_NONE, /* optinfo_flags */
  TV_NONE, /* tv_id */
  PROP_gimple_lcf , /* properties_required */
  0, /* properties_provided */
  0, /* properties_destroyed */
  0, /* todo_flags_start */
  0, /* todo_flags_finish */
};

class pass_warn_exception_inevitable : public gimple_opt_pass
{
public:
  pass_warn_exception_inevitable(gcc::context *ctxt)
    : gimple_opt_pass(pass_data_exception_detection, ctxt)
  {}

  virtual unsigned int execute (function *);
};

/* execute checks the first basic block of function fun to see if it
   calls a runtime exception.  */

unsigned int
pass_warn_exception_inevitable::execute (function *fun)
{
  gimple_stmt_iterator gsi;
  basic_block bb;
  /* Record a function declaration.  */
  rtenode *fn = rtegraph_lookup (fun->gimple_body, fun->decl, false);

  rtegraph_set_current_function (fn);
  /* Check if the current function is a module constructor/deconstructor.
     Also check if the current function is declared as external.  */
  examine_function_decl (fn);

#if defined(DEBUG_BASICBLOCK)
  pretty_function (fun->decl);
  int basic_count = 0;
#endif
  FOR_EACH_BB_FN (bb, fun)
    {
#if defined(DEBUG_BASICBLOCK)
      int stmt_count = 0;
#endif
      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
	{
#if defined(DEBUG_BASICBLOCK)
	  printf ("  [%d][%d]  [basic block][statement]\n",
		  basic_count, stmt_count);
	  stmt_count++;
#endif
	  runtime_exception_inevitable (gsi_stmt (gsi));
#if defined(DEBUG_BASICBLOCK)
	  debug (gsi_stmt (gsi));
#endif
	}
      /* We only care about the first basic block in each function.
         We could continue to search if this edge falls though (top
         of a loop for example) but for now this is cautiously safe.
         --fixme--  */
      return 0;
#if defined(DEBUG_BASICBLOCK)
      basic_count++;
#endif
    }
  return 0;
}

/* analyse_graph discovers any reachable call to a runtime exception in the
   first basic block of a reachable function.  It then calls rtegraph_finish
   to tidy up and return all dynamic memory used.  */

void analyse_graph (void *gcc_data, void *user_data)
{
  rtegraph_discover ();
  rtegraph_finish ();
}

} // anon namespace


static gimple_opt_pass *
make_pass_warn_exception_inevitable (gcc::context *ctxt)
{
  return new pass_warn_exception_inevitable (ctxt);
}


/* plugin_init, check the version and register the plugin.  */

int
plugin_init (struct plugin_name_args *plugin_info,
	     struct plugin_gcc_version *version)
{
  struct register_pass_info pass_info;
  const char *plugin_name = plugin_info->base_name;

  if (!plugin_default_version_check (version, &gcc_version))
    {
      fprintf (stderr, "incorrect GCC version (%s) this plugin was built for GCC version %s\n",
	       version->basever, gcc_version.basever);
      return 1;
    }

  /* Runtime exception inevitable detection.  This plugin is most effective if
     it is run after all optimizations.  This is plugged in at the end of
     gimple range of optimizations.  */
  pass_info.pass = make_pass_warn_exception_inevitable (g);
  pass_info.reference_pass_name = "*warn_function_noreturn";

  pass_info.ref_pass_instance_number = 1;
  pass_info.pos_op = PASS_POS_INSERT_AFTER;

  rtegraph_init ();

  register_callback (plugin_name,
		     PLUGIN_PASS_MANAGER_SETUP,
		     NULL,
		     &pass_info);
  register_callback (plugin_name,
		     PLUGIN_FINISH, analyse_graph, NULL);
  return 0;
}