coretypes.h: Include input.h and as-a.h.
[platform/upstream/gcc.git] / gcc / config / nds32 / nds32-isr.c
1 /* Subroutines used for ISR of Andes NDS32 cpu for GNU compiler
2    Copyright (C) 2012-2015 Free Software Foundation, Inc.
3    Contributed by Andes Technology Corporation.
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
9    by the Free Software Foundation; either version 3, or (at your
10    option) any later version.
11
12    GCC is distributed in the hope that it will be useful, but WITHOUT
13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15    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 /* ------------------------------------------------------------------------ */
22
23 #include "config.h"
24 #include "system.h"
25 #include "coretypes.h"
26 #include "tm.h"
27 #include "alias.h"
28 #include "symtab.h"
29 #include "tree.h"
30 #include "stor-layout.h"
31 #include "varasm.h"
32 #include "calls.h"
33 #include "rtl.h"
34 #include "regs.h"
35 #include "hard-reg-set.h"
36 #include "insn-config.h"        /* Required by recog.h.  */
37 #include "conditions.h"
38 #include "output.h"
39 #include "insn-attr.h"          /* For DFA state_t.  */
40 #include "insn-codes.h"         /* For CODE_FOR_xxx.  */
41 #include "reload.h"             /* For push_reload().  */
42 #include "flags.h"
43 #include "function.h"
44 #include "insn-config.h"
45 #include "expmed.h"
46 #include "dojump.h"
47 #include "explow.h"
48 #include "emit-rtl.h"
49 #include "stmt.h"
50 #include "expr.h"
51 #include "recog.h"
52 #include "diagnostic-core.h"
53 #include "dominance.h"
54 #include "cfg.h"
55 #include "cfgrtl.h"
56 #include "cfganal.h"
57 #include "lcm.h"
58 #include "cfgbuild.h"
59 #include "cfgcleanup.h"
60 #include "predict.h"
61 #include "basic-block.h"
62 #include "df.h"
63 #include "tm_p.h"
64 #include "tm-constrs.h"
65 #include "optabs.h"             /* For GEN_FCN.  */
66 #include "target.h"
67 #include "target-def.h"
68 #include "langhooks.h"          /* For add_builtin_function().  */
69 #include "builtins.h"
70
71 /* ------------------------------------------------------------------------ */
72
73 /* Refer to nds32.h, there are maximum 73 isr vectors in nds32 architecture.
74    0 for reset handler with __attribute__((reset())),
75    1-8 for exception handler with __attribute__((exception(1,...,8))),
76    and 9-72 for interrupt handler with __attribute__((interrupt(0,...,63))).
77    We use an array to record essential information for each vector.  */
78 static struct nds32_isr_info nds32_isr_vectors[NDS32_N_ISR_VECTORS];
79
80 /* ------------------------------------------------------------------------ */
81
82 /* A helper function to emit section head template.  */
83 static void
84 nds32_emit_section_head_template (char section_name[],
85                                   char symbol_name[],
86                                   int align_value,
87                                   bool object_p)
88 {
89   const char *flags_str;
90   const char *type_str;
91
92   flags_str = (object_p) ? "\"a\"" : "\"ax\"";
93   type_str = (object_p) ? "@object" : "@function";
94
95   fprintf (asm_out_file, "\t.section\t%s, %s\n", section_name, flags_str);
96   fprintf (asm_out_file, "\t.align\t%d\n", align_value);
97   fprintf (asm_out_file, "\t.global\t%s\n", symbol_name);
98   fprintf (asm_out_file, "\t.type\t%s, %s\n", symbol_name, type_str);
99   fprintf (asm_out_file, "%s:\n", symbol_name);
100 }
101
102 /* A helper function to emit section tail template.  */
103 static void
104 nds32_emit_section_tail_template (char symbol_name[])
105 {
106   fprintf (asm_out_file, "\t.size\t%s, .-%s\n", symbol_name, symbol_name);
107 }
108
109 /* Function to emit isr jump table section.  */
110 static void
111 nds32_emit_isr_jmptbl_section (int vector_id)
112 {
113   char section_name[100];
114   char symbol_name[100];
115
116   /* Prepare jmptbl section and symbol name.  */
117   snprintf (section_name, sizeof (section_name),
118             ".nds32_jmptbl.%02d", vector_id);
119   snprintf (symbol_name, sizeof (symbol_name),
120             "_nds32_jmptbl_%02d", vector_id);
121
122   nds32_emit_section_head_template (section_name, symbol_name, 2, true);
123   fprintf (asm_out_file, "\t.word\t%s\n",
124                          nds32_isr_vectors[vector_id].func_name);
125   nds32_emit_section_tail_template (symbol_name);
126 }
127
128 /* Function to emit isr vector section.  */
129 static void
130 nds32_emit_isr_vector_section (int vector_id)
131 {
132   unsigned int vector_number_offset = 0;
133   const char *c_str = "CATEGORY";
134   const char *sr_str = "SR";
135   const char *nt_str = "NT";
136   const char *vs_str = "VS";
137   char first_level_handler_name[100];
138   char section_name[100];
139   char symbol_name[100];
140
141   /* Set the vector number offset so that we can calculate
142      the value that user specifies in the attribute.
143      We also prepare the category string for first level handler name.  */
144   switch (nds32_isr_vectors[vector_id].category)
145     {
146     case NDS32_ISR_INTERRUPT:
147       vector_number_offset = 9;
148       c_str = "i";
149       break;
150     case NDS32_ISR_EXCEPTION:
151       vector_number_offset = 0;
152       c_str = "e";
153       break;
154     case NDS32_ISR_NONE:
155     case NDS32_ISR_RESET:
156       /* Normally it should not be here.  */
157       gcc_unreachable ();
158       break;
159     }
160
161   /* Prepare save reg string for first level handler name.  */
162   switch (nds32_isr_vectors[vector_id].save_reg)
163     {
164     case NDS32_SAVE_ALL:
165       sr_str = "sa";
166       break;
167     case NDS32_PARTIAL_SAVE:
168       sr_str = "ps";
169       break;
170     }
171
172   /* Prepare nested type string for first level handler name.  */
173   switch (nds32_isr_vectors[vector_id].nested_type)
174     {
175     case NDS32_NESTED:
176       nt_str = "ns";
177       break;
178     case NDS32_NOT_NESTED:
179       nt_str = "nn";
180       break;
181     case NDS32_NESTED_READY:
182       nt_str = "nr";
183       break;
184     }
185
186   /* Currently we have 4-byte or 16-byte size for each vector.
187      If it is 4-byte, the first level handler name has suffix string "_4b".  */
188   vs_str = (nds32_isr_vector_size == 4) ? "_4b" : "";
189
190   /* Now we can create first level handler name.  */
191   snprintf (first_level_handler_name, sizeof (first_level_handler_name),
192             "_nds32_%s_%s_%s%s", c_str, sr_str, nt_str, vs_str);
193
194   /* Prepare vector section and symbol name.  */
195   snprintf (section_name, sizeof (section_name),
196             ".nds32_vector.%02d", vector_id);
197   snprintf (symbol_name, sizeof (symbol_name),
198             "_nds32_vector_%02d%s", vector_id, vs_str);
199
200
201   /* Everything is ready.  We can start emit vector section content.  */
202   nds32_emit_section_head_template (section_name, symbol_name,
203                                     floor_log2 (nds32_isr_vector_size), false);
204
205   /* According to the vector size, the instructions in the
206      vector section may be different.  */
207   if (nds32_isr_vector_size == 4)
208     {
209       /* This block is for 4-byte vector size.
210          Hardware $VID support is necessary and only one instruction
211          is needed in vector section.  */
212       fprintf (asm_out_file, "\tj\t%s ! jump to first level handler\n",
213                              first_level_handler_name);
214     }
215   else
216     {
217       /* This block is for 16-byte vector size.
218          There is NO hardware $VID so that we need several instructions
219          such as pushing GPRs and preparing software vid at vector section.
220          For pushing GPRs, there are four variations for
221          16-byte vector content and we have to handle each combination.
222          For preparing software vid, note that the vid need to
223          be substracted vector_number_offset.  */
224       if (TARGET_REDUCED_REGS)
225         {
226           if (nds32_isr_vectors[vector_id].save_reg == NDS32_SAVE_ALL)
227             {
228               /* Case of reduced set registers and save_all attribute.  */
229               fprintf (asm_out_file, "\t! reduced set regs + save_all\n");
230               fprintf (asm_out_file, "\tsmw.adm\t$r15, [$sp], $r15, 0xf\n");
231               fprintf (asm_out_file, "\tsmw.adm\t$r0, [$sp], $r10, 0x0\n");
232
233             }
234           else
235             {
236               /* Case of reduced set registers and partial_save attribute.  */
237               fprintf (asm_out_file, "\t! reduced set regs + partial_save\n");
238               fprintf (asm_out_file, "\tsmw.adm\t$r15, [$sp], $r15, 0x2\n");
239               fprintf (asm_out_file, "\tsmw.adm\t$r0, [$sp], $r5, 0x0\n");
240             }
241         }
242       else
243         {
244           if (nds32_isr_vectors[vector_id].save_reg == NDS32_SAVE_ALL)
245             {
246               /* Case of full set registers and save_all attribute.  */
247               fprintf (asm_out_file, "\t! full set regs + save_all\n");
248               fprintf (asm_out_file, "\tsmw.adm\t$r0, [$sp], $r27, 0xf\n");
249             }
250           else
251             {
252               /* Case of full set registers and partial_save attribute.  */
253               fprintf (asm_out_file, "\t! full set regs + partial_save\n");
254               fprintf (asm_out_file, "\tsmw.adm\t$r15, [$sp], $r27, 0x2\n");
255               fprintf (asm_out_file, "\tsmw.adm\t$r0, [$sp], $r5, 0x0\n");
256             }
257         }
258
259       fprintf (asm_out_file, "\tmovi\t$r0, %d ! preparing software vid\n",
260                              vector_id - vector_number_offset);
261       fprintf (asm_out_file, "\tj\t%s ! jump to first level handler\n",
262                              first_level_handler_name);
263     }
264
265   nds32_emit_section_tail_template (symbol_name);
266 }
267
268 /* Function to emit isr reset handler content.
269    Including all jmptbl/vector references, jmptbl section,
270    vector section, nmi handler section, and warm handler section.  */
271 static void
272 nds32_emit_isr_reset_content (void)
273 {
274   unsigned int i;
275   unsigned int total_n_vectors;
276   const char *vs_str;
277   char reset_handler_name[100];
278   char section_name[100];
279   char symbol_name[100];
280
281   total_n_vectors = nds32_isr_vectors[0].total_n_vectors;
282   vs_str = (nds32_isr_vector_size == 4) ? "_4b" : "";
283
284   fprintf (asm_out_file, "\t! RESET HANDLER CONTENT - BEGIN !\n");
285
286   /* Create references in .rodata according to total number of vectors.  */
287   fprintf (asm_out_file, "\t.section\t.rodata\n");
288   fprintf (asm_out_file, "\t.align\t2\n");
289
290   /* Emit jmptbl references.  */
291   fprintf (asm_out_file, "\t ! references to jmptbl section entries\n");
292   for (i = 0; i < total_n_vectors; i++)
293     fprintf (asm_out_file, "\t.word\t_nds32_jmptbl_%02d\n", i);
294
295   /* Emit vector references.  */
296   fprintf (asm_out_file, "\t ! references to vector section entries\n");
297   for (i = 0; i < total_n_vectors; i++)
298     fprintf (asm_out_file, "\t.word\t_nds32_vector_%02d%s\n", i, vs_str);
299
300   /* Emit jmptbl_00 section.  */
301   snprintf (section_name, sizeof (section_name), ".nds32_jmptbl.00");
302   snprintf (symbol_name, sizeof (symbol_name), "_nds32_jmptbl_00");
303
304   fprintf (asm_out_file, "\t! ....................................\n");
305   nds32_emit_section_head_template (section_name, symbol_name, 2, true);
306   fprintf (asm_out_file, "\t.word\t%s\n",
307                          nds32_isr_vectors[0].func_name);
308   nds32_emit_section_tail_template (symbol_name);
309
310   /* Emit vector_00 section.  */
311   snprintf (section_name, sizeof (section_name), ".nds32_vector.00");
312   snprintf (symbol_name, sizeof (symbol_name), "_nds32_vector_00%s", vs_str);
313   snprintf (reset_handler_name, sizeof (reset_handler_name),
314             "_nds32_reset%s", vs_str);
315
316   fprintf (asm_out_file, "\t! ....................................\n");
317   nds32_emit_section_head_template (section_name, symbol_name,
318                                     floor_log2 (nds32_isr_vector_size), false);
319   fprintf (asm_out_file, "\tj\t%s ! jump to reset handler\n",
320                          reset_handler_name);
321   nds32_emit_section_tail_template (symbol_name);
322
323   /* Emit nmi handler section.  */
324   snprintf (section_name, sizeof (section_name), ".nds32_nmih");
325   snprintf (symbol_name, sizeof (symbol_name), "_nds32_nmih");
326
327   fprintf (asm_out_file, "\t! ....................................\n");
328   nds32_emit_section_head_template (section_name, symbol_name, 2, true);
329   fprintf (asm_out_file, "\t.word\t%s\n",
330                          (strlen (nds32_isr_vectors[0].nmi_name) == 0)
331                          ? "0"
332                          : nds32_isr_vectors[0].nmi_name);
333   nds32_emit_section_tail_template (symbol_name);
334
335   /* Emit warm handler section.  */
336   snprintf (section_name, sizeof (section_name), ".nds32_wrh");
337   snprintf (symbol_name, sizeof (symbol_name), "_nds32_wrh");
338
339   fprintf (asm_out_file, "\t! ....................................\n");
340   nds32_emit_section_head_template (section_name, symbol_name, 2, true);
341   fprintf (asm_out_file, "\t.word\t%s\n",
342                          (strlen (nds32_isr_vectors[0].warm_name) == 0)
343                          ? "0"
344                          : nds32_isr_vectors[0].warm_name);
345   nds32_emit_section_tail_template (symbol_name);
346
347   fprintf (asm_out_file, "\t! RESET HANDLER CONTENT - END !\n");
348 }
349
350 /* Function for nds32_merge_decl_attributes() and nds32_insert_attributes()
351    to check if there are any conflict isr-specific attributes being set.
352    We need to check:
353      1. Only 'save_all' or 'partial_save' in the attributes.
354      2. Only 'nested', 'not_nested', or 'nested_ready' in the attributes.
355      3. Only 'interrupt', 'exception', or 'reset' in the attributes.  */
356 void
357 nds32_check_isr_attrs_conflict (tree func_decl, tree func_attrs)
358 {
359   int save_all_p, partial_save_p;
360   int nested_p, not_nested_p, nested_ready_p;
361   int intr_p, excp_p, reset_p;
362
363   /* Initialize variables.  */
364   save_all_p = partial_save_p = 0;
365   nested_p = not_nested_p = nested_ready_p = 0;
366   intr_p = excp_p = reset_p = 0;
367
368   /* We must check at MOST one attribute to set save-reg.  */
369   if (lookup_attribute ("save_all", func_attrs))
370     save_all_p = 1;
371   if (lookup_attribute ("partial_save", func_attrs))
372     partial_save_p = 1;
373
374   if ((save_all_p + partial_save_p) > 1)
375     error ("multiple save reg attributes to function %qD", func_decl);
376
377   /* We must check at MOST one attribute to set nested-type.  */
378   if (lookup_attribute ("nested", func_attrs))
379     nested_p = 1;
380   if (lookup_attribute ("not_nested", func_attrs))
381     not_nested_p = 1;
382   if (lookup_attribute ("nested_ready", func_attrs))
383     nested_ready_p = 1;
384
385   if ((nested_p + not_nested_p + nested_ready_p) > 1)
386     error ("multiple nested types attributes to function %qD", func_decl);
387
388   /* We must check at MOST one attribute to
389      set interrupt/exception/reset.  */
390   if (lookup_attribute ("interrupt", func_attrs))
391     intr_p = 1;
392   if (lookup_attribute ("exception", func_attrs))
393     excp_p = 1;
394   if (lookup_attribute ("reset", func_attrs))
395     reset_p = 1;
396
397   if ((intr_p + excp_p + reset_p) > 1)
398     error ("multiple interrupt attributes to function %qD", func_decl);
399 }
400
401 /* Function to construct isr vectors information array.
402    We DO NOT HAVE TO check if the attributes are valid
403    because those works are supposed to be done on
404    nds32_merge_decl_attributes() and nds32_insert_attributes().  */
405 void
406 nds32_construct_isr_vectors_information (tree func_attrs,
407                                          const char *func_name)
408 {
409   tree save_all, partial_save;
410   tree nested, not_nested, nested_ready;
411   tree intr, excp, reset;
412
413   save_all     = lookup_attribute ("save_all", func_attrs);
414   partial_save = lookup_attribute ("partial_save", func_attrs);
415
416   nested       = lookup_attribute ("nested", func_attrs);
417   not_nested   = lookup_attribute ("not_nested", func_attrs);
418   nested_ready = lookup_attribute ("nested_ready", func_attrs);
419
420   intr  = lookup_attribute ("interrupt", func_attrs);
421   excp  = lookup_attribute ("exception", func_attrs);
422   reset = lookup_attribute ("reset", func_attrs);
423
424   /* If there is no interrupt/exception/reset, we can return immediately.  */
425   if (!intr && !excp && !reset)
426     return;
427
428   /* If we are here, either we have interrupt/exception,
429      or reset attribute.  */
430   if (intr || excp)
431     {
432       tree id_list;
433
434       /* Prepare id list so that we can traverse and set vector id.  */
435       id_list = (intr) ? (TREE_VALUE (intr)) : (TREE_VALUE (excp));
436
437       while (id_list)
438         {
439           tree id;
440           int vector_id;
441           unsigned int vector_number_offset;
442
443           /* The way to handle interrupt or exception is the same,
444              we just need to take care of actual vector number.
445              For interrupt(0..63), the actual vector number is (9..72).
446              For exception(1..8), the actual vector number is (1..8).  */
447           vector_number_offset = (intr) ? (9) : (0);
448
449           /* Pick up each vector id value.  */
450           id = TREE_VALUE (id_list);
451           /* Add vector_number_offset to get actual vector number.  */
452           vector_id = TREE_INT_CST_LOW (id) + vector_number_offset;
453
454           /* Enable corresponding vector and set function name.  */
455           nds32_isr_vectors[vector_id].category = (intr)
456                                                   ? (NDS32_ISR_INTERRUPT)
457                                                   : (NDS32_ISR_EXCEPTION);
458           strcpy (nds32_isr_vectors[vector_id].func_name, func_name);
459
460           /* Set register saving scheme.  */
461           if (save_all)
462             nds32_isr_vectors[vector_id].save_reg = NDS32_SAVE_ALL;
463           else if (partial_save)
464             nds32_isr_vectors[vector_id].save_reg = NDS32_PARTIAL_SAVE;
465
466           /* Set nested type.  */
467           if (nested)
468             nds32_isr_vectors[vector_id].nested_type = NDS32_NESTED;
469           else if (not_nested)
470             nds32_isr_vectors[vector_id].nested_type = NDS32_NOT_NESTED;
471           else if (nested_ready)
472             nds32_isr_vectors[vector_id].nested_type = NDS32_NESTED_READY;
473
474           /* Advance to next id.  */
475           id_list = TREE_CHAIN (id_list);
476         }
477     }
478   else
479     {
480       tree id_list;
481       tree id;
482       tree nmi, warm;
483
484       /* Deal with reset attribute.  Its vector number is always 0.  */
485       nds32_isr_vectors[0].category = NDS32_ISR_RESET;
486
487       /* Prepare id_list and identify id value so that
488          we can set total number of vectors.  */
489       id_list = TREE_VALUE (reset);
490       id = TREE_VALUE (id_list);
491
492       /* The total vectors = interrupt + exception numbers + reset.
493          There are 8 exception and 1 reset in nds32 architecture.  */
494       nds32_isr_vectors[0].total_n_vectors = TREE_INT_CST_LOW (id) + 8 + 1;
495       strcpy (nds32_isr_vectors[0].func_name, func_name);
496
497       /* Retrieve nmi and warm function.  */
498       nmi  = lookup_attribute ("nmi", func_attrs);
499       warm = lookup_attribute ("warm", func_attrs);
500
501       if (nmi != NULL_TREE)
502         {
503           tree nmi_func_list;
504           tree nmi_func;
505
506           nmi_func_list = TREE_VALUE (nmi);
507           nmi_func = TREE_VALUE (nmi_func_list);
508
509           /* Record nmi function name.  */
510           strcpy (nds32_isr_vectors[0].nmi_name,
511                   IDENTIFIER_POINTER (nmi_func));
512         }
513
514       if (warm != NULL_TREE)
515         {
516           tree warm_func_list;
517           tree warm_func;
518
519           warm_func_list = TREE_VALUE (warm);
520           warm_func = TREE_VALUE (warm_func_list);
521
522           /* Record warm function name.  */
523           strcpy (nds32_isr_vectors[0].warm_name,
524                   IDENTIFIER_POINTER (warm_func));
525         }
526     }
527 }
528
529 /* A helper function to handle isr stuff at the beginning of asm file.  */
530 void
531 nds32_asm_file_start_for_isr (void)
532 {
533   int i;
534
535   /* Initialize isr vector information array before compiling functions.  */
536   for (i = 0; i < NDS32_N_ISR_VECTORS; i++)
537     {
538       nds32_isr_vectors[i].category = NDS32_ISR_NONE;
539       strcpy (nds32_isr_vectors[i].func_name, "");
540       nds32_isr_vectors[i].save_reg = NDS32_PARTIAL_SAVE;
541       nds32_isr_vectors[i].nested_type = NDS32_NOT_NESTED;
542       nds32_isr_vectors[i].total_n_vectors = 0;
543       strcpy (nds32_isr_vectors[i].nmi_name, "");
544       strcpy (nds32_isr_vectors[i].warm_name, "");
545     }
546 }
547
548 /* A helper function to handle isr stuff at the end of asm file.  */
549 void
550 nds32_asm_file_end_for_isr (void)
551 {
552   int i;
553
554   /* If all the vectors are NDS32_ISR_NONE, we can return immediately.  */
555   for (i = 0; i < NDS32_N_ISR_VECTORS; i++)
556     if (nds32_isr_vectors[i].category != NDS32_ISR_NONE)
557       break;
558
559   if (i == NDS32_N_ISR_VECTORS)
560     return;
561
562   /* At least one vector is NOT NDS32_ISR_NONE,
563      we should output isr vector information.  */
564   fprintf (asm_out_file, "\t! ------------------------------------\n");
565   fprintf (asm_out_file, "\t! The isr vector information:\n");
566   fprintf (asm_out_file, "\t! ------------------------------------\n");
567
568   /* Check reset handler first.  Its vector number is always 0.  */
569   if (nds32_isr_vectors[0].category == NDS32_ISR_RESET)
570     {
571       nds32_emit_isr_reset_content ();
572       fprintf (asm_out_file, "\t! ------------------------------------\n");
573     }
574
575   /* Check other vectors, starting from vector number 1.  */
576   for (i = 1; i < NDS32_N_ISR_VECTORS; i++)
577     {
578       if (nds32_isr_vectors[i].category == NDS32_ISR_INTERRUPT
579           || nds32_isr_vectors[i].category == NDS32_ISR_EXCEPTION)
580         {
581           /* Found one vector which is interupt or exception.
582              Output its jmptbl and vector section content.  */
583           fprintf (asm_out_file, "\t! interrupt/exception vector %02d\n", i);
584           fprintf (asm_out_file, "\t! ------------------------------------\n");
585           nds32_emit_isr_jmptbl_section (i);
586           fprintf (asm_out_file, "\t! ....................................\n");
587           nds32_emit_isr_vector_section (i);
588           fprintf (asm_out_file, "\t! ------------------------------------\n");
589         }
590     }
591 }
592
593 /* Return true if FUNC is a isr function.  */
594 bool
595 nds32_isr_function_p (tree func)
596 {
597   tree t_intr;
598   tree t_excp;
599   tree t_reset;
600
601   tree attrs;
602
603   if (TREE_CODE (func) != FUNCTION_DECL)
604     abort ();
605
606   attrs = DECL_ATTRIBUTES (func);
607
608   t_intr  = lookup_attribute ("interrupt", attrs);
609   t_excp  = lookup_attribute ("exception", attrs);
610   t_reset = lookup_attribute ("reset", attrs);
611
612   return ((t_intr != NULL_TREE)
613           || (t_excp != NULL_TREE)
614           || (t_reset != NULL_TREE));
615 }
616
617 /* ------------------------------------------------------------------------ */