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