1/* Exception support for GNU CHILL.
2   WARNING:  Only works for native (needs setjmp.h)!  FIXME!
3   Copyright (C) 1992, 93, 1994, 1998 Free Software Foundation, Inc.
4
5This file is part of GNU CC.
6
7GNU CC is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU CC is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU CC; see the file COPYING.  If not, write to
19the Free Software Foundation, 59 Temple Place - Suite 330,
20Boston, MA 02111-1307, USA.  */
21
22#include "config.h"
23#include "system.h"
24
25/* On Suns this can get you to the right definition if you
26   set the right value for TARGET.  */
27#include <setjmp.h>
28#ifdef sequent
29/* Can you believe they forgot this?  */
30#ifndef _JBLEN
31#define _JBLEN 11
32#endif
33#endif
34
35#ifndef _JBLEN
36#define _JBLEN (sizeof(jmp_buf)/sizeof(int))
37#define _JBLEN_2 _JBLEN+20
38#else
39/* if we use i.e. posix threads, this buffer must be longer */
40#define _JBLEN_2 _JBLEN+20
41#endif
42
43/* On Linux setjmp is __setjmp FIXME: what is for CROSS */
44#ifndef SETJMP_LIBRARY_NAME
45#ifdef __linux__
46#define SETJMP_LIBRARY_NAME "__setjmp"
47#else
48#define SETJMP_LIBRARY_NAME "setjmp"
49#endif
50#endif
51
52#include "tree.h"
53#include "ch-tree.h"
54#include "rtl.h"
55#include "toplev.h"
56
57extern int  expand_exit_needed;
58
59static tree link_handler_decl;
60static tree handler_link_pointer_type;
61static tree unlink_handler_decl;
62static int exceptions_initialized = 0;
63static void emit_setup_handler PROTO((void));
64static void initialize_exceptions PROTO((void));
65static tree char_pointer_type_for_handler;
66
67/* If this is 1, operations to push and pop on the __exceptionStack
68   are inline.  The default is is to use a function call, to
69   allow for a per-thread exception stack. */
70static int inline_exception_stack_ops = 0;
71
72struct handler_state
73{
74  struct handler_state *next;
75
76  /* Starts at 0, then incremented for every <on-alternative>. */
77  int prev_on_alternative;
78
79  /* If > 0: handler number for ELSE handler. */
80  int else_handler;
81
82  int action_number;
83
84  char do_pushlevel;
85
86  tree on_alt_list;
87  tree setjmp_expr;
88
89  /* A decl for the static handler array (used to map exception name to int).*/
90  tree handler_array_decl;
91
92  rtx end_label;
93
94  /* Used to pass a tree from emit_setup_handler to chill_start_on. */
95  tree handler_ref;
96
97  tree unlink_cleanup;
98
99  tree function;
100
101  /* flag to indicate that we are currently compiling this handler.
102     is_handled will need this to determine an unhandled exception */
103  int compiling;
104};
105
106/* This is incremented by one each time we start an action which
107   might have an ON-handler.  It is reset between passes. */
108static int action_number = 0;
109
110int action_nesting_level = 0;
111
112/* The global_handler_list is constructed in pass 1.  It is not sorted.
113   It contains one element for each action that actually had an ON-handler.
114   An element's ACTION_NUMBER matches the action_number
115   of that action.  The global_handler_list is eaten up during pass 2. */
116#define ACTION_NUMBER(HANDLER) ((HANDLER)->action_number)
117struct handler_state *global_handler_list = NULL;
118
119/* This is a stack of handlers, one for each nested ON-handler. */
120static struct handler_state *current_handler = NULL;
121
122static struct handler_state *free_handlers = NULL; /* freelist */
123
124static tree handler_element_type;
125static tree handler_link_type;
126static tree BISJ;
127static tree jbuf_ident, prev_ident, handlers_ident;
128static tree exception_stack_decl = 0;
129
130/* Chain of cleanups assocated with exception handlers.
131   The TREE_PURPOSE is an INTEGER_CST whose value is the
132   DECL_ACTION_NESTING_LEVEL (when the handled actions was entered).
133   The TREE_VALUE is an expression to expand when we exit that action. */
134
135static tree cleanup_chain = NULL_TREE;
136
137#if 0
138/* Merge the current sequence onto the tail of the previous one. */
139
140void
141pop_sequence ()
142{
143  rtx sequence_first = get_insns ();
144
145  end_sequence ();
146  emit_insns (sequence_first);
147
148}
149#endif
150
151/* Things we need to do at the beginning of pass 2. */
152
153void
154except_init_pass_2 ()
155{
156  /* First sort the global_handler_list on ACTION_NUMBER.
157     This will already be in close to reverse order (the exception being
158     nested ON-handlers), so insertion sort should essentially linear. */
159
160  register struct handler_state *old_list = global_handler_list;
161
162  /* First add a dummy final element. */
163  if (free_handlers)
164    global_handler_list = free_handlers;
165  else
166    global_handler_list
167      = (struct handler_state*) permalloc (sizeof (struct handler_state));
168  /* Make the final dummy "larger" than any other element. */
169  ACTION_NUMBER (global_handler_list) = action_number + 1;
170  /* Now move all the elements in old_list over to global_handler_list. */
171  while (old_list != NULL)
172    {
173      register struct handler_state **ptr = &global_handler_list;
174      /* Unlink from old_list. */
175      register struct handler_state *current = old_list;
176      old_list = old_list->next;
177
178      while (ACTION_NUMBER (current) > ACTION_NUMBER (*ptr))
179	ptr = &(*ptr)->next;
180      /* Link into proper place in global_handler_list (new list). */
181      current->next = *ptr;
182      *ptr = current;
183    }
184
185  /* Don't forget to reset action_number. */
186  action_number = 0;
187}
188
189/* This function is called at the beginning of an action that might be
190   followed by an ON-handler.  Chill syntax doesn't let us know if
191   we actually have an ON-handler until we see the ON, so we save
192   away during pass 1 that information for use during pass 2. */
193
194void
195push_handler ()
196{
197  register struct handler_state *hstate;
198
199  action_number++;
200  action_nesting_level++;
201
202  if (pass == 1)
203    {
204      if (free_handlers)
205	{
206	  hstate = free_handlers;
207	  free_handlers = hstate->next;
208	}
209      else
210	{
211	  hstate =
212	    (struct handler_state*) permalloc (sizeof (struct handler_state));
213	}
214
215      hstate->next = current_handler;
216      current_handler = hstate;
217      hstate->prev_on_alternative = 0;
218      hstate->else_handler = 0;
219      hstate->on_alt_list = NULL_TREE;
220      hstate->compiling = 0;
221
222      ACTION_NUMBER (hstate) = action_number;
223      return;
224    }
225
226  if (ACTION_NUMBER (global_handler_list) != action_number)
227    return;
228
229  /* OK.  This action actually has an ON-handler.
230     Pop it from global_handler_list, and use it. */
231
232  hstate = global_handler_list;
233  global_handler_list = hstate->next;
234
235  /* Since this is pass 2, let's generate prologue code for that. */
236
237  hstate->next = current_handler;
238  current_handler = hstate;
239
240  hstate->prev_on_alternative = 0;
241  hstate->function = current_function_decl;
242
243  emit_setup_handler ();
244}
245
246static tree
247start_handler_array ()
248{
249  tree handler_array_type, decl;
250
251  push_obstacks_nochange ();
252  end_temporary_allocation ();
253  handler_array_type = build_array_type (handler_element_type, NULL_TREE);
254  decl = build_lang_decl (VAR_DECL,
255			  get_unique_identifier ("handler_table"),
256			  handler_array_type);
257
258/*  TREE_TYPE (decl) = handler_array_type;*/
259  TREE_READONLY (decl) = 1;
260  TREE_STATIC (decl) = 1;
261  DECL_INITIAL (decl) = error_mark_node;
262
263  pushdecl (decl);
264  make_decl_rtl (decl, NULL_PTR, 0);
265  current_handler->handler_array_decl = decl;
266  return decl;
267}
268
269static void
270finish_handler_array ()
271{
272  tree decl = current_handler->handler_array_decl;
273  tree t;
274  tree handler_array_init = NULL_TREE;
275  int handlers_count = 1;
276  int nelts;
277
278  /* Build the table mapping exceptions to handler(-number)s.
279     This is done in reverse order. */
280
281  /* First push the end of the list.  This is either the ELSE
282     handler (current_handler->else_handler>0) or NULL handler to indicate
283     the end of the list (if current_handler->else-handler == 0).
284     The following works either way. */
285  handler_array_init = build_tree_list
286    (NULL_TREE, chill_expand_tuple
287     (handler_element_type,
288      build_nt (CONSTRUCTOR, NULL_TREE,
289		tree_cons (NULL_TREE,
290			   null_pointer_node,
291			   build_tree_list (NULL_TREE,
292					    build_int_2 (current_handler->else_handler,
293							     0))))));
294
295  for (t = current_handler->on_alt_list; t != NULL_TREE; t = TREE_CHAIN (t))
296    { tree handler_number = TREE_PURPOSE(t);
297      tree elist = TREE_VALUE (t);
298      for ( ; elist != NULL_TREE; elist = TREE_CHAIN (elist))
299	{
300	  tree ex_decl =
301	    build_chill_exception_decl (IDENTIFIER_POINTER(TREE_VALUE(elist)));
302	  tree ex_addr = build1 (ADDR_EXPR,
303				 char_pointer_type_for_handler,
304				 ex_decl);
305	  tree el = build_nt (CONSTRUCTOR, NULL_TREE,
306			      tree_cons (NULL_TREE,
307					 ex_addr,
308					 build_tree_list (NULL_TREE,
309							  handler_number)));
310	  mark_addressable (ex_decl);
311	  TREE_CONSTANT (ex_addr) = 1;
312	  handler_array_init =
313	    tree_cons (NULL_TREE,
314		       chill_expand_tuple (handler_element_type, el),
315		       handler_array_init);
316	  handlers_count++;
317	}
318    }
319
320#if 1
321  nelts = list_length (handler_array_init);
322  TYPE_DOMAIN (TREE_TYPE (decl))
323    = build_index_type (build_int_2 (nelts - 1, - (nelts == 0)));
324  layout_type (TREE_TYPE (decl));
325  DECL_INITIAL (decl)
326    = convert (TREE_TYPE (decl),
327	       build_nt (CONSTRUCTOR, NULL_TREE, handler_array_init));
328
329  /* Pop back to the obstack that is current for this binding level.
330     This is because MAXINDEX, rtl, etc. to be made below
331     must go in the permanent obstack.  But don't discard the
332     temporary data yet.  */
333  pop_obstacks ();
334  layout_decl (decl, 0);
335  /* To prevent make_decl_rtl (called indiectly by rest_of_decl_compilation)
336     throwing the existing RTL (which has already been used). */
337  PUT_MODE (DECL_RTL (decl), DECL_MODE (decl));
338  rest_of_decl_compilation (decl, (char*)0, 0, 0);
339  expand_decl_init (decl);
340#else
341  /* To prevent make_decl_rtl (called indirectly by finish_decl)
342     altering the existing RTL. */
343  GET_MODE (DECL_RTL (current_handler->handler_array_decl)) =
344    DECL_MODE (current_handler->handler_array_decl);
345
346  finish_decl (current_handler->handler_array_decl,
347	       build_nt (CONSTRUCTOR, NULL_TREE, handler_array_init),
348	       NULL_TREE);
349#endif
350}
351
352
353void
354pop_handler (used)
355     int used;
356{
357  action_nesting_level--;
358  if (pass == 1)
359    {
360      struct handler_state *old = current_handler;
361      if (old == NULL)
362	fatal ("internal error: on stack out of sync");
363      current_handler = old->next;
364
365      if (used)
366	{ /* Push unto global_handler_list. */
367	  old->next = global_handler_list;
368	  global_handler_list = old;
369	}
370      else
371	{
372	  /* Push onto free_handlers free list. */
373	  old->next = free_handlers;
374	  free_handlers = old;
375	}
376    }
377  else if (used)
378    {
379      current_handler = current_handler->next;
380    }
381}
382
383/* Emit code before an action that has an ON-handler. */
384
385static void
386emit_setup_handler ()
387{
388  tree handler_decl, handler_addr, t;
389
390  /* Field references. */
391  tree jbuf_ref, handlers_ref,prev_ref;
392  if (!exceptions_initialized)
393    {
394      /* We temporarily reset the maximum_field_alignment to zero so the
395	 compiler's exception data structures can be compatible with the
396	 run-time system, even when we're compiling with -fpack. */
397      extern int maximum_field_alignment;
398      int save_maximum_field_alignment = maximum_field_alignment;
399      maximum_field_alignment = 0;
400      push_obstacks_nochange ();
401      end_temporary_allocation ();
402      initialize_exceptions ();
403      pop_obstacks ();
404      maximum_field_alignment = save_maximum_field_alignment;
405    }
406
407  push_momentary ();
408
409  handler_decl = build_lang_decl (VAR_DECL,
410				  get_unique_identifier ("handler"),
411				  handler_link_type);
412  push_obstacks_nochange ();
413  pushdecl(handler_decl);
414  expand_decl (handler_decl);
415  finish_decl (handler_decl);
416
417  jbuf_ref = build_component_ref (handler_decl, jbuf_ident);
418  jbuf_ref = build_chill_arrow_expr (jbuf_ref, 1);
419  handlers_ref = build_component_ref (handler_decl, handlers_ident);
420  prev_ref = build_component_ref (handler_decl, prev_ident);
421
422  /* Emit code to link in handler in __exceptionStack chain. */
423  mark_addressable (handler_decl);
424  handler_addr = build1 (ADDR_EXPR, handler_link_pointer_type, handler_decl);
425  if (inline_exception_stack_ops)
426    {
427      expand_expr_stmt (build_chill_modify_expr (prev_ref,
428						 exception_stack_decl));
429      expand_expr_stmt (build_chill_modify_expr (exception_stack_decl,
430						 handler_addr));
431      current_handler->handler_ref = prev_ref;
432    }
433  else
434    {
435      expand_expr_stmt (build_chill_function_call (link_handler_decl,
436					     build_tree_list (NULL_TREE,
437							      handler_addr)));
438      current_handler->handler_ref = handler_addr;
439    }
440
441  /* Expand:  handler->__handlers = { <<array mapping names to ints } */
442  t =  build1 (NOP_EXPR, build_pointer_type (handler_element_type),
443	       build_chill_arrow_expr (start_handler_array (), 1));
444  expand_expr_stmt (build_chill_modify_expr (handlers_ref, t));
445
446  /* Emit code to unlink handler. */
447  if (inline_exception_stack_ops)
448    current_handler->unlink_cleanup
449      = build_chill_modify_expr (exception_stack_decl,
450				 current_handler->handler_ref);
451  else
452    current_handler->unlink_cleanup
453      = build_chill_function_call (unlink_handler_decl,
454				   build_tree_list(NULL_TREE,
455					       current_handler->handler_ref));
456  cleanup_chain = tree_cons (build_int_2 (action_nesting_level, 0),
457			     current_handler->unlink_cleanup,
458			     cleanup_chain);
459
460  /* Emit code for setjmp. */
461
462  current_handler->setjmp_expr =
463    build_chill_function_call (BISJ, build_tree_list (NULL_TREE, jbuf_ref));
464  expand_start_case (1, current_handler->setjmp_expr,
465		     integer_type_node, "on handler");
466
467  chill_handle_case_label (integer_zero_node, current_handler->setjmp_expr);
468}
469
470/* Start emitting code for: <actions> ON <handlers> END.
471   Assume we've parsed <actions>, and the setup needed for it. */
472
473void
474chill_start_on ()
475{
476  expand_expr_stmt (current_handler->unlink_cleanup);
477
478  /* Emit code to jump past the handlers. */
479  current_handler->end_label = gen_label_rtx ();
480  current_handler->compiling = 1;
481  emit_jump (current_handler->end_label);
482}
483
484void
485chill_finish_on ()
486{
487  expand_end_case (current_handler->setjmp_expr);
488
489  finish_handler_array ();
490
491  emit_label (current_handler->end_label);
492
493  pop_momentary ();
494
495  cleanup_chain = TREE_CHAIN (cleanup_chain);
496}
497
498void
499chill_handle_on_labels (labels)
500     tree labels;
501{
502  int alternative = ++current_handler->prev_on_alternative;
503  if (pass == 1)
504    {
505      tree handler_number = build_int_2 (alternative, 0);
506      current_handler->on_alt_list =
507	tree_cons (handler_number, labels, current_handler->on_alt_list);
508    }
509  else
510    {
511      /* Find handler_number saved in pass 1. */
512      tree tmp = current_handler->on_alt_list;
513      while (TREE_INT_CST_LOW (TREE_PURPOSE (tmp)) != alternative)
514	tmp = TREE_CHAIN (tmp);
515      if (expand_exit_needed)
516	expand_exit_something (), expand_exit_needed = 0;
517      chill_handle_case_label (TREE_PURPOSE (tmp),
518			       current_handler->setjmp_expr);
519    }
520}
521
522void
523chill_start_default_handler ()
524{
525  current_handler->else_handler = ++current_handler->prev_on_alternative;
526  if (!ignoring)
527    {
528      chill_handle_case_default ();
529    }
530}
531
532void
533chill_check_no_handlers ()
534{
535  if (current_handler != NULL)
536    fatal ("internal error: on stack not empty when done");
537}
538
539static void
540initialize_exceptions ()
541{
542  tree jmp_buf_type = build_array_type (integer_type_node,
543					build_index_type (build_int_2 (_JBLEN_2-1, 0)));
544  tree setjmp_fndecl, link_ftype;
545  tree parmtypes
546    = tree_cons (NULL_TREE, build_pointer_type (jmp_buf_type), void_list_node);
547
548  setjmp_fndecl = builtin_function ("setjmp",
549				    build_function_type (integer_type_node,
550							 parmtypes),
551				    NOT_BUILT_IN,
552				    SETJMP_LIBRARY_NAME);
553  BISJ = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (setjmp_fndecl)),
554		 setjmp_fndecl);
555
556  char_pointer_type_for_handler
557    = build_pointer_type (build_type_variant (char_type_node, 1, 0));
558  handler_element_type =
559    build_chill_struct_type (chainon
560			     (build_decl (FIELD_DECL,
561					  get_identifier("__exceptid"),
562					  char_pointer_type_for_handler),
563			      build_decl (FIELD_DECL,
564					  get_identifier("__handlerno"),
565					  integer_type_node)));
566
567  jbuf_ident = get_identifier("__jbuf");
568  prev_ident = get_identifier("__prev");
569  handlers_ident = get_identifier("__handlers");
570
571  handler_link_type =
572    build_chill_struct_type
573      (chainon
574       (build_decl (FIELD_DECL, prev_ident, ptr_type_node),
575	chainon
576	(build_decl (FIELD_DECL, handlers_ident,
577		     build_pointer_type (handler_element_type)),
578	 build_decl (FIELD_DECL, jbuf_ident, jmp_buf_type))));
579
580  handler_link_pointer_type = build_pointer_type (handler_link_type);
581
582  if (inline_exception_stack_ops)
583    {
584      exception_stack_decl =
585	build_lang_decl (VAR_DECL,
586			 get_identifier("__exceptionStack"),
587			 handler_link_pointer_type);
588      TREE_STATIC (exception_stack_decl) = 1;
589      TREE_PUBLIC (exception_stack_decl) = 1;
590      DECL_EXTERNAL (exception_stack_decl) = 1;
591      push_obstacks_nochange ();
592      pushdecl(exception_stack_decl);
593      make_decl_rtl (exception_stack_decl, NULL_PTR, 1);
594      finish_decl (exception_stack_decl);
595    }
596
597  link_ftype = build_function_type (void_type_node,
598				    tree_cons (NULL_TREE,
599					       handler_link_pointer_type,
600					       void_list_node));
601  link_handler_decl = builtin_function ("__ch_link_handler", link_ftype,
602					NOT_BUILT_IN, NULL_PTR);
603  unlink_handler_decl = builtin_function ("__ch_unlink_handler", link_ftype,
604					  NOT_BUILT_IN, NULL_PTR);
605
606  exceptions_initialized = 1;
607}
608
609/* Do the cleanup(s) needed for a GOTO label.
610   We only need to do the last of the cleanups. */
611
612void
613expand_goto_except_cleanup (label_level)
614     int label_level;
615{
616  tree list = cleanup_chain;
617  tree last = NULL_TREE;
618  for ( ; list != NULL_TREE; list = TREE_CHAIN (list))
619    {
620      if (TREE_INT_CST_LOW (TREE_PURPOSE (list)) > label_level)
621	last = list;
622      else
623	break;
624    }
625  if (last)
626    expand_expr_stmt (TREE_VALUE (last));
627}
628
629/* Returns true if there is a valid handler for EXCEPT_NAME
630   in the current static scope.
631   0 ... no handler found
632   1 ... local handler available
633   2 ... function may propagate this exception
634*/
635
636int
637is_handled (except_name)
638     tree except_name;
639{
640  tree t;
641  struct handler_state *h = current_handler;
642
643  /* if we are are currently compiling this handler
644     we have to start at the next level */
645  if (h && h->compiling)
646    h = h->next;
647  while (h != NULL)
648    {
649      if (h->function != current_function_decl)
650	break;
651      if (h->else_handler > 0)
652	return 1;
653      for (t = h->on_alt_list; t != NULL_TREE; t = TREE_CHAIN (t))
654	{
655	  if (value_member (except_name, TREE_VALUE (t)))
656	    return 1;
657	}
658      h = h->next;
659    }
660
661  t = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl));
662
663  if (value_member (except_name, t))
664    return 2;
665  return 0;
666}
667
668/* function generates code to reraise exceptions
669   for PROC's propagating exceptions. */
670
671void
672chill_reraise_exceptions (exceptions)
673     tree exceptions;
674{
675  tree wrk;
676
677  if (exceptions == NULL_TREE)
678    return; /* just in case */
679
680  if (pass == 1)
681    {
682      for (wrk = exceptions; wrk != NULL_TREE; wrk = TREE_CHAIN (wrk))
683	chill_handle_on_labels (build_tree_list (NULL_TREE, TREE_VALUE (wrk)));
684    }
685  else /* pass == 2 */
686    {
687      chill_start_on ();
688      expand_exit_needed = 0;
689
690      for (wrk = exceptions; wrk != NULL_TREE; wrk = TREE_CHAIN (wrk))
691	{
692	  chill_handle_on_labels (TREE_VALUE (wrk));
693	  /* do a CAUSE exception */
694	  expand_expr_stmt (build_cause_exception (TREE_VALUE (wrk), 0));
695	  expand_exit_needed = 1;
696	}
697      chill_finish_on ();
698    }
699  pop_handler (1);
700}
701