|
|
1.1 root 1: /* Handle exceptional things in C++.
2: Copyright (C) 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
3: Contributed by Michael Tiemann <[email protected]>
4: Rewritten by Mike Stump <[email protected]>, based upon an
5: initial re-implementation courtesy Tad Hunt.
6:
7: This file is part of GNU CC.
8:
9: GNU CC is free software; you can redistribute it and/or modify
10: it under the terms of the GNU General Public License as published by
11: the Free Software Foundation; either version 2, or (at your option)
12: any later version.
13:
14: GNU CC is distributed in the hope that it will be useful,
15: but WITHOUT ANY WARRANTY; without even the implied warranty of
16: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: GNU General Public License for more details.
18:
19: You should have received a copy of the GNU General Public License
20: along with GNU CC; see the file COPYING. If not, write to
21: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
22:
23:
24: /* High-level class interface. */
25:
26: #include "config.h"
27: #include "tree.h"
28: #include "rtl.h"
29: #include "cp-tree.h"
30: #include "flags.h"
31: #include "obstack.h"
32: #include "expr.h"
33:
34: extern void (*interim_eh_hook) PROTO((tree));
35:
36: /* holds the fndecl for __builtin_return_address () */
37: tree builtin_return_address_fndecl;
38:
39: /* Define at your own risk! */
40: #ifndef CROSS_COMPILE
41: #ifdef sun
42: #ifdef sparc
43: #define TRY_NEW_EH
44: #endif
45: #endif
46: #endif
47:
48: #ifndef TRY_NEW_EH
49:
50: static void
51: sorry_no_eh ()
52: {
53: static int warned = 0;
54: if (! warned)
55: {
56: sorry ("exception handling not supported");
57: warned = 1;
58: }
59: }
60:
61: void
62: expand_exception_blocks ()
63: {
64: }
65:
66: void
67: start_protect ()
68: {
69: }
70:
71: void
72: end_protect (finalization)
73: tree finalization;
74: {
75: }
76:
77: void
78: expand_start_try_stmts ()
79: {
80: sorry_no_eh ();
81: }
82:
83: void
84: expand_end_try_stmts ()
85: {
86: }
87:
88: void
89: expand_start_all_catch ()
90: {
91: }
92:
93: void
94: expand_end_all_catch ()
95: {
96: }
97:
98: void
99: expand_start_catch_block (declspecs, declarator)
100: tree declspecs, declarator;
101: {
102: }
103:
104: void
105: expand_end_catch_block ()
106: {
107: }
108:
109: void
110: init_exception_processing ()
111: {
112: }
113:
114: void
115: expand_throw (exp)
116: tree exp;
117: {
118: sorry_no_eh ();
119: }
120:
121: #else
122:
123: static int
124: doing_eh (do_warn)
125: int do_warn;
126: {
127: if (! flag_handle_exceptions)
128: {
129: static int warned = 0;
130: if (! warned && do_warn)
131: {
132: error ("exception handling disabled, use -fhandle-exceptions to enable.");
133: warned = 1;
134: }
135: return 0;
136: }
137: return 1;
138: }
139:
140:
141: /*
142: NO GNEWS IS GOOD GNEWS WITH GARRY GNUS: This version is much closer
143: to supporting exception handling as per Stroustrup's 2nd edition.
144: It is a complete rewrite of all the EH stuff that was here before
145: Shortcomings:
146: 1. The type of the throw and catch must still match
147: exactly (no support yet for matching base classes)
148: 2. Throw specifications of functions still doesnt't work.
149: Cool Things:
150: 1. Destructors are called properly :-)
151: 2. No overhead for the non-exception thrown case.
152: 3. Fixing shortcomings 1 and 2 is simple.
153: -Tad Hunt ([email protected])
154:
155: */
156:
157: /* A couple of backend routines from m88k.c */
158:
159: /* used to cache a call to __builtin_return_address () */
160: static tree BuiltinReturnAddress;
161:
162:
163:
164:
165:
166: #include <stdio.h>
167:
168: /* XXX - Tad: for EH */
169: /* output an exception table entry */
170:
171: static void
172: output_exception_table_entry (file, start_label, end_label, eh_label)
173: FILE *file;
174: rtx start_label, end_label, eh_label;
175: {
176: char label[100];
177:
178: assemble_integer (start_label, BITS_PER_WORD/BITS_PER_UNIT, 1);
179: assemble_integer (end_label, BITS_PER_WORD/BITS_PER_UNIT, 1);
180: assemble_integer (eh_label, BITS_PER_WORD/BITS_PER_UNIT, 1);
181: putc ('\n', file); /* blank line */
182: }
183:
184: static void
185: easy_expand_asm (str)
186: char *str;
187: {
188: expand_asm (build_string (strlen (str)+1, str));
189: }
190:
191: /* unwind the stack. */
192: static void
193: do_unwind (throw_label)
194: rtx throw_label;
195: {
196: #ifdef sparc
197: extern FILE *asm_out_file;
198: tree fcall;
199: tree params;
200: rtx return_val_rtx;
201:
202: /* call to __builtin_return_address () */
203: params=tree_cons (NULL_TREE, integer_zero_node, NULL_TREE);
204: fcall = build_function_call (BuiltinReturnAddress, params);
205: return_val_rtx = expand_expr (fcall, NULL_RTX, SImode, 0);
206: /* In the return, the new pc is pc+8, as the value comming in is
207: really the address of the call insn, not the next insn. */
208: emit_move_insn (return_val_rtx, plus_constant(gen_rtx (LABEL_REF,
209: Pmode,
210: throw_label), -8));
211: /* We use three values, PC, type, and value */
212: easy_expand_asm ("st %l0,[%fp]");
213: easy_expand_asm ("st %l1,[%fp+4]");
214: easy_expand_asm ("st %l2,[%fp+8]");
215: easy_expand_asm ("ret");
216: easy_expand_asm ("restore");
217: emit_barrier ();
218: #endif
219: #if m88k
220: rtx temp_frame = frame_pointer_rtx;
221:
222: temp_frame = memory_address (Pmode, temp_frame);
223: temp_frame = copy_to_reg (gen_rtx (MEM, Pmode, temp_frame));
224:
225: /* hopefully this will successfully pop the frame! */
226: emit_move_insn (frame_pointer_rtx, temp_frame);
227: emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
228: emit_move_insn (arg_pointer_rtx, frame_pointer_rtx);
229: emit_insn (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode,
230: (HOST_WIDE_INT)m88k_debugger_offset (stack_pointer_rtx, 0))));
231:
232: #if 0
233: emit_insn (gen_add2_insn (arg_pointer_rtx, gen_rtx (CONST_INT, VOIDmode,
234: -(HOST_WIDE_INT)m88k_debugger_offset (arg_pointer_rtx, 0))));
235:
236: emit_move_insn (stack_pointer_rtx, arg_pointer_rtx);
237:
238: emit_insn (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode,
239: (HOST_WIDE_INT)m88k_debugger_offset (arg_pointer_rtx, 0))));
240: #endif
241: #endif
242: }
243:
244:
245:
246: #if 0
247: /* This is the startup, and finish stuff per exception table. */
248:
249: /* XXX - Tad: exception handling section */
250: #ifndef EXCEPT_SECTION_ASM_OP
251: #define EXCEPT_SECTION_ASM_OP "section\t.gcc_except_table,\"a\",@progbits"
252: #endif
253:
254: #ifdef EXCEPT_SECTION_ASM_OP
255: typedef struct {
256: void *start_protect;
257: void *end_protect;
258: void *exception_handler;
259: } exception_table;
260: #endif /* EXCEPT_SECTION_ASM_OP */
261:
262: #ifdef EXCEPT_SECTION_ASM_OP
263:
264: /* on machines which support it, the exception table lives in another section,
265: but it needs a label so we can reference it... This sets up that
266: label! */
267: asm (EXCEPT_SECTION_ASM_OP);
268: exception_table __EXCEPTION_TABLE__[1] = { (void*)0, (void*)0, (void*)0 };
269: asm (TEXT_SECTION_ASM_OP);
270:
271: #endif /* EXCEPT_SECTION_ASM_OP */
272:
273: #ifdef EXCEPT_SECTION_ASM_OP
274:
275: /* we need to know where the end of the exception table is... so this
276: is how we do it! */
277:
278: asm (EXCEPT_SECTION_ASM_OP);
279: exception_table __EXCEPTION_END__[1] = { (void*)-1, (void*)-1, (void*)-1 };
280: asm (TEXT_SECTION_ASM_OP);
281:
282: #endif /* EXCEPT_SECTION_ASM_OP */
283:
284: #endif
285:
286: void
287: exception_section ()
288: {
289: #ifdef ASM_OUTPUT_SECTION_NAME
290: named_section (".gcc_except_table");
291: #else
292: text_section ();
293: #endif
294: }
295:
296:
297:
298:
299: /* from: my-cp-except.c */
300:
301: /* VI: ":set ts=4" */
302: #if 0
303: #include <stdio.h> */
304: #include "config.h"
305: #include "tree.h"
306: #include "rtl.h"
307: #include "cp-tree.h"
308: #endif
309: #include "decl.h"
310: #if 0
311: #include "flags.h"
312: #endif
313: #include "insn-flags.h"
314: #include "obstack.h"
315: #if 0
316: #include "expr.h"
317: #endif
318:
319: /* ======================================================================
320: Briefly the algorithm works like this:
321:
322: When a constructor or start of a try block is encountered,
323: push_eh_entry (&eh_stack) is called. Push_eh_entry () creates a
324: new entry in the unwind protection stack and returns a label to
325: output to start the protection for that block.
326:
327: When a destructor or end try block is encountered, pop_eh_entry
328: (&eh_stack) is called. Pop_eh_entry () returns the ehEntry it
329: created when push_eh_entry () was called. The ehEntry structure
330: contains three things at this point. The start protect label,
331: the end protect label, and the exception handler label. The end
332: protect label should be output before the call to the destructor
333: (if any). If it was a destructor, then its parse tree is stored
334: in the finalization variable in the ehEntry structure. Otherwise
335: the finalization variable is set to NULL to reflect the fact that
336: is the the end of a try block. Next, this modified ehEntry node
337: is enqueued in the finalizations queue by calling
338: enqueue_eh_entry (&queue,entry).
339:
340: +---------------------------------------------------------------+
341: |XXX: Will need modification to deal with partially |
342: | constructed arrays of objects |
343: | |
344: | Basically, this consists of keeping track of how many |
345: | of the objects have been constructed already (this |
346: | should be in a register though, so that shouldn't be a |
347: | problem. |
348: +---------------------------------------------------------------+
349:
350: When a catch block is encountered, there is a lot of work to be
351: done.
352:
353: Since we don't want to generate the catch block inline with the
354: regular flow of the function, we need to have some way of doing
355: so. Luckily, we have a couple of routines "get_last_insn ()" and
356: "set_last_insn ()" provided. When the start of a catch block is
357: encountered, we save a pointer to the last insn generated. After
358: the catch block is generated, we save a pointer to the first
359: catch block insn and the last catch block insn with the routines
360: "NEXT_INSN ()" and "get_last_insn ()". We then set the last insn
361: to be the last insn generated before the catch block, and set the
362: NEXT_INSN (last_insn) to zero.
363:
364: Since catch blocks might be nested inside other catch blocks, and
365: we munge the chain of generated insns after the catch block is
366: generated, we need to store the pointers to the last insn
367: generated in a stack, so that when the end of a catch block is
368: encountered, the last insn before the current catch block can be
369: popped and set to be the last insn, and the first and last insns
370: of the catch block just generated can be enqueue'd for output at
371: a later time.
372:
373: Next we must insure that when the catch block is executed, all
374: finalizations for the matching try block have been completed. If
375: any of those finalizations throw an exception, we must call
376: terminate according to the ARM (section r.15.6.1). What this
377: means is that we need to dequeue and emit finalizations for each
378: entry in the ehQueue until we get to an entry with a NULL
379: finalization field. For any of the finalization entries, if it
380: is not a call to terminate (), we must protect it by giving it
381: another start label, end label, and exception handler label,
382: setting its finalization tree to be a call to terminate (), and
383: enqueue'ing this new ehEntry to be output at an outer level.
384: Finally, after all that is done, we can get around to outputting
385: the catch block which basically wraps all the "catch (...) {...}"
386: statements in a big if/then/else construct that matches the
387: correct block to call.
388:
389: ===================================================================== */
390:
391: extern rtx emit_insn PROTO((rtx));
392: extern rtx gen_nop PROTO(());
393:
394: /* local globals for function calls
395: ====================================================================== */
396:
397: /* used to cache "terminate ()", "unexpected ()", "set_terminate ()", and
398: "set_unexpected ()" after default_conversion. (lib-except.c) */
399: static tree Terminate, Unexpected, SetTerminate, SetUnexpected, CatchMatch;
400:
401: /* used to cache __find_first_exception_table_match ()
402: for throw (lib-except.c) */
403: static tree FirstExceptionMatch;
404:
405: /* used to cache a call to __unwind_function () (lib-except.c) */
406: static tree Unwind;
407:
408: /* holds a ready to emit call to "terminate ()". */
409: static tree TerminateFunctionCall;
410:
411: /* ====================================================================== */
412:
413:
414:
415: /* data structures for my various quick and dirty stacks and queues
416: Eventually, most of this should go away, because I think it can be
417: integrated with stuff already built into the compiler. */
418:
419: /* =================================================================== */
420:
421: struct labelNode {
422: rtx label;
423: struct labelNode *chain;
424: };
425:
426:
427: /* this is the most important structure here. Basically this is how I store
428: an exception table entry internally. */
429: struct ehEntry {
430: rtx start_label;
431: rtx end_label;
432: rtx exception_handler_label;
433:
434: tree finalization;
435: };
436:
437: struct ehNode {
438: struct ehEntry *entry;
439: struct ehNode *chain;
440: };
441:
442: struct ehStack {
443: struct ehNode *top;
444: };
445:
446: struct ehQueue {
447: struct ehNode *head;
448: struct ehNode *tail;
449: };
450:
451: struct exceptNode {
452: rtx catchstart;
453: rtx catchend;
454:
455: struct exceptNode *chain;
456: };
457:
458: struct exceptStack {
459: struct exceptNode *top;
460: };
461: /* ========================================================================= */
462:
463:
464:
465: /* local globals - these local globals are for storing data necessary for
466: generating the exception table and code in the correct order.
467:
468: ========================================================================= */
469:
470: /* Holds the pc for doing "throw" */
471: rtx saved_pc;
472: /* Holds the type of the thing being thrown. */
473: rtx saved_throw_type;
474: /* Holds the value being thrown. */
475: rtx saved_throw_value;
476:
477: rtx throw_label;
478:
479: static struct ehStack ehstack;
480: static struct ehQueue ehqueue;
481: static struct ehQueue eh_table_output_queue;
482: static struct exceptStack exceptstack;
483: static struct labelNode *false_label_stack = NULL;
484: static struct labelNode *caught_return_label_stack = NULL;
485: /* ========================================================================= */
486:
487: /* function prototypes */
488: static struct ehEntry *pop_eh_entry PROTO((struct ehStack *stack));
489: static void enqueue_eh_entry PROTO((struct ehQueue *queue, struct ehEntry *entry));
490: static void push_except_stmts PROTO((struct exceptStack *exceptstack,
491: rtx catchstart, rtx catchend));
492: static int pop_except_stmts PROTO((struct exceptStack *exceptstack,
493: rtx *catchstart, rtx *catchend));
494: static rtx push_eh_entry PROTO((struct ehStack *stack));
495: static struct ehEntry *dequeue_eh_entry PROTO((struct ehQueue *queue));
496: static void new_eh_queue PROTO((struct ehQueue *queue));
497: static void new_eh_stack PROTO((struct ehStack *stack));
498: static void new_except_stack PROTO((struct exceptStack *queue));
499: static void push_last_insn PROTO(());
500: static rtx pop_last_insn PROTO(());
501: static void push_label_entry PROTO((struct labelNode **labelstack, rtx label));
502: static rtx pop_label_entry PROTO((struct labelNode **labelstack));
503: static rtx top_label_entry PROTO((struct labelNode **labelstack));
504: static struct ehEntry *copy_eh_entry PROTO((struct ehEntry *entry));
505:
506:
507:
508: /* All my cheesy stack/queue/misc data structure handling routines
509:
510: ========================================================================= */
511:
512: static void
513: push_label_entry (labelstack, label)
514: struct labelNode **labelstack;
515: rtx label;
516: {
517: struct labelNode *newnode=(struct labelNode*)xmalloc (sizeof (struct labelNode));
518:
519: newnode->label = label;
520: newnode->chain = *labelstack;
521: *labelstack = newnode;
522: }
523:
524: static rtx
525: pop_label_entry (labelstack)
526: struct labelNode **labelstack;
527: {
528: rtx label;
529: struct labelNode *tempnode;
530:
531: if (! *labelstack) return NULL_RTX;
532:
533: tempnode = *labelstack;
534: label = tempnode->label;
535: *labelstack = (*labelstack)->chain;
536: free (tempnode);
537:
538: return label;
539: }
540:
541: static rtx
542: top_label_entry (labelstack)
543: struct labelNode **labelstack;
544: {
545: if (! *labelstack) return NULL_RTX;
546:
547: return (*labelstack)->label;
548: }
549:
550: static void
551: push_except_stmts (exceptstack, catchstart, catchend)
552: struct exceptStack *exceptstack;
553: rtx catchstart, catchend;
554: {
555: struct exceptNode *newnode = (struct exceptNode*)
556: xmalloc (sizeof (struct exceptNode));
557:
558: newnode->catchstart = catchstart;
559: newnode->catchend = catchend;
560: newnode->chain = exceptstack->top;
561:
562: exceptstack->top = newnode;
563: }
564:
565: static int
566: pop_except_stmts (exceptstack, catchstart, catchend)
567: struct exceptStack *exceptstack;
568: rtx *catchstart, *catchend;
569: {
570: struct exceptNode *tempnode;
571:
572: if (!exceptstack->top) {
573: *catchstart = *catchend = NULL_RTX;
574: return 0;
575: }
576:
577: tempnode = exceptstack->top;
578: exceptstack->top = exceptstack->top->chain;
579:
580: *catchstart = tempnode->catchstart;
581: *catchend = tempnode->catchend;
582: free (tempnode);
583:
584: return 1;
585: }
586:
587: /* Push to permanent obstack for rtl generation.
588: One level only! */
589: static struct obstack *saved_rtl_obstack;
590: void
591: push_rtl_perm ()
592: {
593: extern struct obstack permanent_obstack;
594: extern struct obstack *rtl_obstack;
595:
596: saved_rtl_obstack = rtl_obstack;
597: rtl_obstack = &permanent_obstack;
598: }
599:
600: /* Pop back to normal rtl handling. */
601: static void
602: pop_rtl_from_perm ()
603: {
604: extern struct obstack permanent_obstack;
605: extern struct obstack *rtl_obstack;
606:
607: rtl_obstack = saved_rtl_obstack;
608: }
609:
610: static rtx
611: push_eh_entry (stack)
612: struct ehStack *stack;
613: {
614: struct ehNode *node = (struct ehNode*)xmalloc (sizeof (struct ehNode));
615: struct ehEntry *entry = (struct ehEntry*)xmalloc (sizeof (struct ehEntry));
616:
617: if (stack == NULL) {
618: free (node);
619: free (entry);
620: return NULL_RTX;
621: }
622:
623: /* These are saved for the exception table. */
624: push_rtl_perm ();
625: entry->start_label = gen_label_rtx ();
626: entry->end_label = gen_label_rtx ();
627: entry->exception_handler_label = gen_label_rtx ();
628: pop_rtl_from_perm ();
629:
630: entry->finalization = NULL_TREE;
631:
632: node->entry = entry;
633: node->chain = stack->top;
634: stack->top = node;
635:
636: enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (entry));
637:
638: return entry->start_label;
639: }
640:
641: static struct ehEntry *
642: pop_eh_entry (stack)
643: struct ehStack *stack;
644: {
645: struct ehNode *tempnode;
646: struct ehEntry *tempentry;
647:
648: if (stack && (tempnode = stack->top)) {
649: tempentry = tempnode->entry;
650: stack->top = stack->top->chain;
651: free (tempnode);
652:
653: return tempentry;
654: }
655:
656: return NULL;
657: }
658:
659: static struct ehEntry *
660: copy_eh_entry (entry)
661: struct ehEntry *entry;
662: {
663: struct ehEntry *newentry;
664:
665: newentry = (struct ehEntry*)xmalloc (sizeof (struct ehEntry));
666: memcpy ((void*)newentry, (void*)entry, sizeof (struct ehEntry));
667:
668: return newentry;
669: }
670:
671: static void
672: enqueue_eh_entry (queue, entry)
673: struct ehQueue *queue;
674: struct ehEntry *entry;
675: {
676: struct ehNode *node = (struct ehNode*)xmalloc (sizeof (struct ehNode));
677:
678: node->entry = entry;
679: node->chain = NULL;
680:
681: if (queue->head == NULL)
682: {
683: queue->head = node;
684: }
685: else
686: {
687: queue->tail->chain = node;
688: }
689: queue->tail = node;
690: }
691:
692: static struct ehEntry *
693: dequeue_eh_entry (queue)
694: struct ehQueue *queue;
695: {
696: struct ehNode *tempnode;
697: struct ehEntry *tempentry;
698:
699: if (queue->head == NULL)
700: return NULL;
701:
702: tempnode = queue->head;
703: queue->head = queue->head->chain;
704:
705: tempentry = tempnode->entry;
706: free (tempnode);
707:
708: return tempentry;
709: }
710:
711: static void
712: new_eh_queue (queue)
713: struct ehQueue *queue;
714: {
715: queue->head = queue->tail = NULL;
716: }
717:
718: static void
719: new_eh_stack (stack)
720: struct ehStack *stack;
721: {
722: stack->top = NULL;
723: }
724:
725: static void
726: new_except_stack (stack)
727: struct exceptStack *stack;
728: {
729: stack->top = NULL;
730: }
731: /* ========================================================================= */
732:
733: void
734: lang_interim_eh (finalization)
735: tree finalization;
736: {
737: if (finalization)
738: end_protect (finalization);
739: else
740: start_protect ();
741: }
742:
743: /* sets up all the global eh stuff that needs to be initialized at the
744: start of compilation.
745:
746: This includes:
747: - Setting up all the function call trees
748: - Initializing the ehqueue
749: - Initializing the eh_table_output_queue
750: - Initializing the ehstack
751: - Initializing the exceptstack
752: */
753:
754: void
755: init_exception_processing ()
756: {
757: extern tree define_function ();
758: tree unexpected_fndecl, terminate_fndecl;
759: tree set_unexpected_fndecl, set_terminate_fndecl;
760: tree catch_match_fndecl;
761: tree find_first_exception_match_fndecl;
762: tree unwind_fndecl;
763: tree temp, PFV;
764:
765: interim_eh_hook = lang_interim_eh;
766:
767: /* void (*)() */
768: PFV = build_pointer_type (build_function_type (void_type_node, void_list_node));
769:
770: /* arg list for the build_function_type call for set_terminate () and
771: set_unexpected () */
772: temp = tree_cons (NULL_TREE, PFV, void_list_node);
773:
774: push_lang_context (lang_name_c);
775:
776: set_terminate_fndecl =
777: define_function ("set_terminate",
778: build_function_type (PFV, temp),
779: NOT_BUILT_IN,
780: pushdecl,
781: 0);
782: set_unexpected_fndecl =
783: define_function ("set_unexpected",
784: build_function_type (PFV, temp),
785: NOT_BUILT_IN,
786: pushdecl,
787: 0);
788:
789: unexpected_fndecl =
790: define_function ("unexpected",
791: build_function_type (void_type_node, void_list_node),
792: NOT_BUILT_IN,
793: pushdecl,
794: 0);
795: terminate_fndecl =
796: define_function ("terminate",
797: build_function_type (void_type_node, void_list_node),
798: NOT_BUILT_IN,
799: pushdecl,
800: 0);
801: catch_match_fndecl =
802: define_function ("__throw_type_match",
803: build_function_type (integer_type_node,
804: tree_cons (NULL_TREE, string_type_node, tree_cons (NULL_TREE, ptr_type_node, void_list_node))),
805: NOT_BUILT_IN,
806: pushdecl,
807: 0);
808: find_first_exception_match_fndecl =
809: define_function ("__find_first_exception_table_match",
810: build_function_type (ptr_type_node,
811: tree_cons (NULL_TREE, ptr_type_node,
812: void_list_node)),
813: NOT_BUILT_IN,
814: pushdecl,
815: 0);
816: unwind_fndecl =
817: define_function ("__unwind_function",
818: build_function_type (void_type_node,
819: tree_cons (NULL_TREE, ptr_type_node, void_list_node)),
820: NOT_BUILT_IN,
821: pushdecl,
822: 0);
823:
824: Unexpected = default_conversion (unexpected_fndecl);
825: Terminate = default_conversion (terminate_fndecl);
826: SetTerminate = default_conversion (set_terminate_fndecl);
827: SetUnexpected = default_conversion (set_unexpected_fndecl);
828: CatchMatch = default_conversion (catch_match_fndecl);
829: FirstExceptionMatch = default_conversion (find_first_exception_match_fndecl);
830: Unwind = default_conversion (unwind_fndecl);
831: BuiltinReturnAddress = default_conversion (builtin_return_address_fndecl);
832:
833: TerminateFunctionCall = build_function_call (Terminate, NULL_TREE);
834:
835: pop_lang_context ();
836: throw_label = gen_label_rtx ();
837: saved_pc = gen_rtx (REG, Pmode, 16);
838: saved_throw_type = gen_rtx (REG, Pmode, 17);
839: saved_throw_value = gen_rtx (REG, Pmode, 18);
840:
841: new_eh_queue (&ehqueue);
842: new_eh_queue (&eh_table_output_queue);
843: new_eh_stack (&ehstack);
844: new_except_stack (&exceptstack);
845: }
846:
847: /* call this to begin a block of unwind protection (ie: when an object is
848: constructed) */
849: void
850: start_protect ()
851: {
852: if (doing_eh (0))
853: {
854: emit_label (push_eh_entry (&ehstack));
855: }
856: }
857:
858: /* call this to end a block of unwind protection. the finalization tree is
859: the finalization which needs to be run in order to cleanly unwind through
860: this level of protection. (ie: call this when a scope is exited)*/
861: void
862: end_protect (finalization)
863: tree finalization;
864: {
865: struct ehEntry *entry = pop_eh_entry (&ehstack);
866:
867: if (! doing_eh (0))
868: return;
869:
870: emit_label (entry->end_label);
871:
872: entry->finalization = finalization;
873:
874: enqueue_eh_entry (&ehqueue, entry);
875: }
876:
877: /* call this on start of a try block. */
878: void
879: expand_start_try_stmts ()
880: {
881: if (doing_eh (1))
882: {
883: start_protect ();
884: }
885: }
886:
887: void
888: expand_end_try_stmts ()
889: {
890: end_protect (integer_zero_node);
891: }
892:
893: struct insn_save_node {
894: rtx last;
895: struct insn_save_node *chain;
896: };
897:
898: static struct insn_save_node *InsnSave = NULL;
899:
900:
901: /* Used to keep track of where the catch blocks start. */
902: static void
903: push_last_insn ()
904: {
905: struct insn_save_node *newnode = (struct insn_save_node*)
906: xmalloc (sizeof (struct insn_save_node));
907:
908: newnode->last = get_last_insn ();
909: newnode->chain = InsnSave;
910: InsnSave = newnode;
911: }
912:
913: /* Use to keep track of where the catch blocks start. */
914: static rtx
915: pop_last_insn ()
916: {
917: struct insn_save_node *tempnode;
918: rtx temprtx;
919:
920: if (!InsnSave) return NULL_RTX;
921:
922: tempnode = InsnSave;
923: temprtx = tempnode->last;
924: InsnSave = InsnSave->chain;
925:
926: free (tempnode);
927:
928: return temprtx;
929: }
930:
931: /* call this to start processing of all the catch blocks. */
932: void
933: expand_start_all_catch ()
934: {
935: struct ehEntry *entry;
936: rtx label;
937:
938: if (! doing_eh (1))
939: return;
940:
941: emit_line_note (input_filename, lineno);
942: label = gen_label_rtx ();
943: /* The label for the exception handling block we will save. */
944: emit_label (label);
945:
946: push_label_entry (&caught_return_label_stack, label);
947:
948: /* Remember where we started. */
949: push_last_insn ();
950:
951: emit_insn (gen_nop ());
952:
953: /* Will this help us not stomp on it? */
954: emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
955: emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
956:
957: while (1)
958: {
959: entry = dequeue_eh_entry (&ehqueue);
960: emit_label (entry->exception_handler_label);
961:
962: expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
963:
964: /* When we get down to the matching entry, stop. */
965: if (entry->finalization == integer_zero_node)
966: break;
967:
968: free (entry);
969: }
970:
971: /* This goes when the below moves out of our way. */
972: #if 1
973: label = gen_label_rtx ();
974: emit_jump (label);
975: #endif
976:
977: /* All this should be out of line, and saved back in the exception handler
978: block area. */
979: #if 1
980: entry->start_label = entry->exception_handler_label;
981: /* These are saved for the exception table. */
982: push_rtl_perm ();
983: entry->end_label = gen_label_rtx ();
984: entry->exception_handler_label = gen_label_rtx ();
985: entry->finalization = TerminateFunctionCall;
986: pop_rtl_from_perm ();
987: emit_label (entry->end_label);
988:
989: enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (entry));
990:
991: /* After running the finalization, continue on out to the next
992: cleanup, if we have nothing better to do. */
993: emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, entry->end_label));
994: /* Will this help us not stomp on it? */
995: emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
996: emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
997: emit_jump (throw_label);
998: emit_label (entry->exception_handler_label);
999: expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
1000: emit_barrier ();
1001: #endif
1002: emit_label (label);
1003: }
1004:
1005: /* call this to end processing of all the catch blocks. */
1006: void
1007: expand_end_all_catch ()
1008: {
1009: rtx catchstart, catchend, last;
1010: rtx label;
1011:
1012: if (! doing_eh (1))
1013: return;
1014:
1015: /* Find the start of the catch block. */
1016: last = pop_last_insn ();
1017: catchstart = NEXT_INSN (last);
1018: catchend = get_last_insn ();
1019:
1020: NEXT_INSN (last) = 0;
1021: set_last_insn (last);
1022:
1023: /* this level of catch blocks is done, so set up the successful catch jump
1024: label for the next layer of catch blocks. */
1025: pop_label_entry (&caught_return_label_stack);
1026:
1027: push_except_stmts (&exceptstack, catchstart, catchend);
1028:
1029: /* Here we fall through into the continuation code. */
1030: }
1031:
1032:
1033: /* this is called from expand_exception_blocks () to expand the toplevel
1034: finalizations for a function. */
1035: void
1036: expand_leftover_cleanups ()
1037: {
1038: struct ehEntry *entry;
1039: rtx first_label = NULL_RTX;
1040:
1041: if (! doing_eh (0))
1042: return;
1043:
1044: /* Will this help us not stomp on it? */
1045: emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
1046: emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
1047:
1048: while ((entry = dequeue_eh_entry (&ehqueue)) != 0)
1049: {
1050: if (! first_label)
1051: first_label = entry->exception_handler_label;
1052: emit_label (entry->exception_handler_label);
1053:
1054: expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
1055:
1056: /* leftover try block, opps. */
1057: if (entry->finalization == integer_zero_node)
1058: abort ();
1059:
1060: free (entry);
1061: }
1062: if (first_label)
1063: {
1064: rtx label;
1065: struct ehEntry entry;
1066: /* These are saved for the exception table. */
1067: push_rtl_perm ();
1068: label = gen_label_rtx ();
1069: entry.start_label = first_label;
1070: entry.end_label = label;
1071: entry.exception_handler_label = gen_label_rtx ();
1072: entry.finalization = TerminateFunctionCall;
1073: pop_rtl_from_perm ();
1074: emit_label (label);
1075:
1076: enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (&entry));
1077:
1078: /* After running the finalization, continue on out to the next
1079: cleanup, if we have nothing better to do. */
1080: emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, entry.end_label));
1081: /* Will this help us not stomp on it? */
1082: emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type));
1083: emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value));
1084: emit_jump (throw_label);
1085: emit_label (entry.exception_handler_label);
1086: expand_expr (entry.finalization, const0_rtx, VOIDmode, 0);
1087: emit_barrier ();
1088: }
1089: }
1090:
1091: /* call this to start a catch block. Typename is the typename, and identifier
1092: is the variable to place the object in or NULL if the variable doesn't
1093: matter. If typename is NULL, that means its a "catch (...)" or catch
1094: everything. In that case we don't need to do any type checking.
1095: (ie: it ends up as the "else" clause rather than an "else if" clause) */
1096: void
1097: expand_start_catch_block (declspecs, declarator)
1098: tree declspecs, declarator;
1099: {
1100: rtx false_label_rtx;
1101: rtx protect_label_rtx;
1102: tree type;
1103: tree decl;
1104: tree init;
1105:
1106: if (! doing_eh (1))
1107: return;
1108:
1109: /* Create a binding level for the parm. */
1110: expand_start_bindings (0);
1111:
1112: if (declspecs)
1113: {
1114: tree init_type;
1115: decl = grokdeclarator (declarator, declspecs, NORMAL, 1, NULL_TREE);
1116:
1117: /* Figure out the type that the initializer is. */
1118: init_type = TREE_TYPE (decl);
1119: if (TREE_CODE (init_type) != REFERENCE_TYPE)
1120: init_type = build_reference_type (init_type);
1121:
1122: init = convert_from_reference (save_expr (make_tree (init_type, saved_throw_value)));
1123:
1124: /* Do we need the below two lines? */
1125: /* Let `finish_decl' know that this initializer is ok. */
1126: DECL_INITIAL (decl) = init;
1127: /* This needs to be preallocated under the try block,
1128: in a union of all catch variables. */
1129: pushdecl (decl);
1130: type = TREE_TYPE (decl);
1131:
1132: /* peel back references, so they match. */
1133: if (TREE_CODE (type) == REFERENCE_TYPE)
1134: type = TREE_TYPE (type);
1135: }
1136: else
1137: type = NULL_TREE;
1138:
1139: /* These are saved for the exception table. */
1140: push_rtl_perm ();
1141: false_label_rtx = gen_label_rtx ();
1142: protect_label_rtx = gen_label_rtx ();
1143: pop_rtl_from_perm ();
1144: push_label_entry (&false_label_stack, false_label_rtx);
1145: push_label_entry (&false_label_stack, protect_label_rtx);
1146:
1147: if (type)
1148: {
1149: tree params;
1150: char *typestring;
1151: rtx call_rtx, return_value_rtx;
1152: tree catch_match_fcall;
1153: tree catchmatch_arg, argval;
1154:
1155: typestring = build_overload_name (type, 1, 1);
1156:
1157: params = tree_cons (NULL_TREE,
1158: combine_strings (build_string (strlen (typestring)+1, typestring)),
1159: tree_cons (NULL_TREE,
1160: make_tree (ptr_type_node, saved_throw_type),
1161: NULL_TREE));
1162: catch_match_fcall = build_function_call (CatchMatch, params);
1163: call_rtx = expand_call (catch_match_fcall, NULL_RTX, 0);
1164:
1165: return_value_rtx =
1166: hard_function_value (integer_type_node, catch_match_fcall);
1167:
1168: /* did the throw type match function return TRUE? */
1169: emit_cmp_insn (return_value_rtx, const0_rtx, NE, NULL_RTX,
1170: GET_MODE (return_value_rtx), 0, 0);
1171:
1172: /* if it returned FALSE, jump over the catch block, else fall into it */
1173: emit_jump_insn (gen_bne (false_label_rtx));
1174: finish_decl (decl, init, NULL_TREE, 0);
1175: }
1176: else
1177: {
1178: /* Fall into the catch all section. */
1179: }
1180:
1181: /* This is the starting of something to protect. */
1182: emit_label (protect_label_rtx);
1183:
1184: emit_line_note (input_filename, lineno);
1185: }
1186:
1187:
1188: /* Call this to end a catch block. Its responsible for emitting the
1189: code to handle jumping back to the correct place, and for emitting
1190: the label to jump to if this catch block didn't match. */
1191: void expand_end_catch_block ()
1192: {
1193: if (doing_eh (1))
1194: {
1195: rtx start_protect_label_rtx;
1196: rtx end_protect_label_rtx;
1197: tree decls;
1198: struct ehEntry entry;
1199:
1200: /* label we jump to if we caught the exception */
1201: emit_jump (top_label_entry (&caught_return_label_stack));
1202:
1203: /* Code to throw out to outer context, if we get an throw from within
1204: our catch handler. */
1205: /* These are saved for the exception table. */
1206: push_rtl_perm ();
1207: entry.exception_handler_label = gen_label_rtx ();
1208: pop_rtl_from_perm ();
1209: emit_label (entry.exception_handler_label);
1210: emit_move_insn (saved_pc, gen_rtx (LABEL_REF,
1211: Pmode,
1212: top_label_entry (&caught_return_label_stack)));
1213: emit_jump (throw_label);
1214: /* No associated finalization. */
1215: entry.finalization = NULL_TREE;
1216:
1217: /* Because we are reordered out of line, we have to protect this. */
1218: /* label for the start of the protection region. */
1219: start_protect_label_rtx = pop_label_entry (&false_label_stack);
1220:
1221: /* Cleanup the EH paramater. */
1222: expand_end_bindings (decls = getdecls (), decls != NULL_TREE, 0);
1223:
1224: /* label we emit to jump to if this catch block didn't match. */
1225: emit_label (end_protect_label_rtx = pop_label_entry (&false_label_stack));
1226:
1227: /* Because we are reordered out of line, we have to protect this. */
1228: entry.start_label = start_protect_label_rtx;
1229: entry.end_label = end_protect_label_rtx;
1230:
1231: /* These set up a call to throw the caught exception into the outer
1232: context. */
1233: enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (&entry));
1234: }
1235: }
1236:
1237: /* cheesyness to save some typing. returns the return value rtx */
1238: rtx
1239: do_function_call (func, params, return_type)
1240: tree func, params, return_type;
1241: {
1242: tree func_call;
1243: func_call = build_function_call (func, params);
1244: expand_call (func_call, NULL_RTX, 0);
1245: if (return_type != NULL_TREE)
1246: return hard_function_value (return_type, func_call);
1247: return NULL_RTX;
1248: }
1249:
1250:
1251: /* is called from expand_excpetion_blocks () to generate the code in a function
1252: to "throw" if anything in the function needs to preform a throw.
1253:
1254: expands "throw" as the following psuedo code:
1255:
1256: throw:
1257: eh = find_first_exception_match (saved_pc);
1258: if (!eh) goto gotta_rethrow_it;
1259: goto eh;
1260:
1261: gotta_rethrow_it:
1262: saved_pc = __builtin_return_address (0);
1263: pop_to_previous_level ();
1264: goto throw;
1265:
1266: */
1267: static void
1268: expand_builtin_throw ()
1269: {
1270: tree fcall;
1271: tree params;
1272: rtx return_val_rtx;
1273: rtx gotta_rethrow_it = gen_label_rtx ();
1274: rtx gotta_call_terminate = gen_label_rtx ();
1275: rtx unwind_and_throw = gen_label_rtx ();
1276: rtx goto_unwind_and_throw = gen_label_rtx ();
1277:
1278: emit_label (throw_label);
1279:
1280: /* search for an exception handler for the saved_pc */
1281: return_val_rtx = do_function_call (FirstExceptionMatch,
1282: tree_cons (NULL_TREE, make_tree (ptr_type_node, saved_pc), NULL_TREE),
1283: ptr_type_node);
1284:
1285: /* did we find one? */
1286: emit_cmp_insn (return_val_rtx, const0_rtx, EQ, NULL_RTX,
1287: GET_MODE (return_val_rtx), 0, 0);
1288:
1289: /* if not, jump to gotta_rethrow_it */
1290: emit_jump_insn (gen_beq (gotta_rethrow_it));
1291:
1292: /* we found it, so jump to it */
1293: emit_indirect_jump (return_val_rtx);
1294:
1295: /* code to deal with unwinding and looking for it again */
1296: emit_label (gotta_rethrow_it);
1297:
1298: /* call to __builtin_return_address () */
1299: params=tree_cons (NULL_TREE, integer_zero_node, NULL_TREE);
1300: fcall = build_function_call (BuiltinReturnAddress, params);
1301: return_val_rtx = expand_expr (fcall, NULL_RTX, SImode, 0);
1302:
1303: /* did __builtin_return_address () return a valid address? */
1304: emit_cmp_insn (return_val_rtx, const0_rtx, EQ, NULL_RTX,
1305: GET_MODE (return_val_rtx), 0, 0);
1306:
1307: emit_jump_insn (gen_beq (gotta_call_terminate));
1308:
1309: /* yes it did */
1310: emit_move_insn (saved_pc, return_val_rtx);
1311: do_unwind (throw_label);
1312: emit_jump (throw_label);
1313:
1314: /* no it didn't --> therefore we need to call terminate */
1315: emit_label (gotta_call_terminate);
1316: do_function_call (Terminate, NULL_TREE, NULL_TREE);
1317: }
1318:
1319:
1320: /* This is called to expand all the toplevel exception handling
1321: finalization for a function. It should only be called once per
1322: function. */
1323: void
1324: expand_exception_blocks ()
1325: {
1326: rtx catchstart, catchend;
1327: rtx last;
1328: static rtx funcend;
1329:
1330: funcend = gen_label_rtx ();
1331: emit_jump (funcend);
1332: /* expand_null_return (); */
1333:
1334: while (pop_except_stmts (&exceptstack, &catchstart, &catchend)) {
1335: last = get_last_insn ();
1336: NEXT_INSN (last) = catchstart;
1337: PREV_INSN (catchstart) = last;
1338: NEXT_INSN (catchend) = 0;
1339: set_last_insn (catchend);
1340: }
1341:
1342: expand_leftover_cleanups ();
1343:
1344: {
1345: static int have_done = 0;
1346: if (! have_done && TREE_PUBLIC (current_function_decl)
1347: && ! DECL_INLINE (current_function_decl))
1348: {
1349: have_done = 1;
1350: expand_builtin_throw ();
1351: }
1352: }
1353: emit_label (funcend);
1354: }
1355:
1356:
1357: /* call this to expand a throw statement. This follows the following
1358: algorithm:
1359:
1360: 1. Allocate space to save the current PC onto the stack.
1361: 2. Generate and emit a label and save its address into the
1362: newly allocate stack space since we can't save the pc directly.
1363: 3. If this is the first call to throw in this function:
1364: generate a label for the throw block
1365: 4. jump to the throw block label. */
1366: void
1367: expand_throw (exp)
1368: tree exp;
1369: {
1370: rtx label;
1371: tree type;
1372:
1373: if (! doing_eh (1))
1374: return;
1375:
1376: /* This is the label that represents where in the code we were, when
1377: we got an exception. This needs to be updated when we rethrow an
1378: exception, so that the matching routine knows to search out. */
1379: label = gen_label_rtx ();
1380: emit_label (label);
1381: emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, label));
1382:
1383: if (exp)
1384: {
1385: /* throw expression */
1386: /* First, decay it. */
1387: exp = default_conversion (exp);
1388: type = TREE_TYPE (exp);
1389:
1390: {
1391: char *typestring = build_overload_name (type, 1, 1);
1392: tree throw_type = build1 (ADDR_EXPR, ptr_type_node, combine_strings (build_string (strlen (typestring)+1, typestring)));
1393: rtx throw_type_rtx = expand_expr (throw_type, NULL_RTX, VOIDmode, 0);
1394: rtx throw_value_rtx;
1395:
1396: emit_move_insn (saved_throw_type, throw_type_rtx);
1397: exp = convert_to_reference (build_reference_type (build_type_variant (TREE_TYPE (exp), 1, 0)), exp, CONV_STATIC, LOOKUP_COMPLAIN, NULL_TREE);
1398: if (exp == error_mark_node)
1399: error (" in thrown expression");
1400: throw_value_rtx = expand_expr (build_unary_op (ADDR_EXPR, exp, 0), NULL_RTX, VOIDmode, 0);
1401: emit_move_insn (saved_throw_value, throw_value_rtx);
1402: }
1403: }
1404: else
1405: {
1406: /* rethrow current exception */
1407: /* This part is easy, as we dont' have to do anything else. */
1408: }
1409:
1410: emit_jump (throw_label);
1411: }
1412:
1413: /* end of: my-cp-except.c */
1414: #endif
1415:
1416:
1417: /* Output the exception table.
1418: Return the number of handlers. */
1419: int
1420: build_exception_table ()
1421: {
1422: int count = 0;
1423: #ifdef TRY_NEW_EH
1424: extern FILE *asm_out_file;
1425: struct ehEntry *entry;
1426: tree eh_node_decl;
1427:
1428: if (! doing_eh (0))
1429: return 0;
1430:
1431: while (entry = dequeue_eh_entry (&eh_table_output_queue))
1432: {
1433: if (count == 0)
1434: {
1435: exception_section ();
1436:
1437: /* Beginning marker for table. */
1438: ASM_OUTPUT_ALIGN (asm_out_file, 2);
1439: ASM_OUTPUT_LABEL (asm_out_file, "__EXCEPTION_TABLE__");
1440: fprintf (asm_out_file, " .word 0, 0, 0\n");
1441: }
1442: count++;
1443: output_exception_table_entry (asm_out_file,
1444: entry->start_label, entry->end_label,
1445: entry->exception_handler_label);
1446: }
1447:
1448: if (count)
1449: {
1450: /* Ending marker for table. */
1451: ASM_OUTPUT_LABEL (asm_out_file, "__EXCEPTION_END__");
1452: fprintf (asm_out_file, " .word -1, -1, -1\n");
1453: }
1454:
1455: #endif /* TRY_NEW_EH */
1456: return count;
1457: }
1458:
1459: void
1460: register_exception_table ()
1461: {
1462: #ifdef TRY_NEW_EH
1463: emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__register_exceptions"), 0,
1464: VOIDmode, 1,
1465: gen_rtx (SYMBOL_REF, Pmode, "__EXCEPTION_TABLE__"),
1466: Pmode);
1467: #endif /* TRY_NEW_EH */
1468: }
1469:
1470: /* Build a throw expression. */
1471: tree
1472: build_throw (e)
1473: tree e;
1474: {
1475: e = build1 (THROW_EXPR, void_type_node, e);
1476: TREE_SIDE_EFFECTS (e) = 1;
1477: return e;
1478: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.