#include "config.h"
#include "system.h"
#include "tree.h"
#include "rtl.h"
#include "cp-tree.h"
#include "flags.h"
#include "obstack.h"
#include "expr.h"
#include "output.h"
#include "except.h"
#include "function.h"
#include "defaults.h"
#include "toplev.h"
#include "eh-common.h"
rtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx));
tree builtin_return_address_fndecl;
static void push_eh_cleanup PROTO((void));
static tree build_eh_type_type PROTO((tree));
static tree build_eh_type PROTO((tree));
static void expand_end_eh_spec PROTO((tree));
static tree call_eh_info PROTO((void));
static void push_eh_info PROTO((void));
static tree get_eh_info PROTO((void));
static tree get_eh_value PROTO((void));
#if 0
static tree get_eh_type PROTO((void));
static tree get_eh_caught PROTO((void));
static tree get_eh_handlers PROTO((void));
#endif
static tree do_pop_exception PROTO((void));
static void process_start_catch_block PROTO((tree, tree));
static tree build_eh_type_type_ref PROTO((tree));
static tree build_terminate_handler PROTO((void));
static tree alloc_eh_object PROTO((tree));
#if 0
#ifndef EXCEPT_SECTION_ASM_OP
#define EXCEPT_SECTION_ASM_OP "section\t.gcc_except_table,\"a\",@progbits"
#endif
#ifdef EXCEPT_SECTION_ASM_OP
asm (EXCEPT_SECTION_ASM_OP);
exception_table __EXCEPTION_TABLE__[1] = { (void*)0, (void*)0, (void*)0 };
asm (TEXT_SECTION_ASM_OP);
#endif
#ifdef EXCEPT_SECTION_ASM_OP
asm (EXCEPT_SECTION_ASM_OP);
exception_table __EXCEPTION_END__[1] = { (void*)-1, (void*)-1, (void*)-1 };
asm (TEXT_SECTION_ASM_OP);
#endif
#endif
#include "decl.h"
#include "insn-flags.h"
#include "obstack.h"
static tree Terminate, CatchMatch;
static tree FirstExceptionMatch;
static tree Unwind;
extern rtx catch_clauses;
extern tree const_ptr_type_node;
void
init_exception_processing ()
{
tree vtype = build_function_type (void_type_node, void_list_node);
if (flag_honor_std)
push_namespace (get_identifier ("std"));
Terminate = auto_function (get_identifier ("terminate"),
vtype, NOT_BUILT_IN);
TREE_THIS_VOLATILE (Terminate) = 1;
if (flag_honor_std)
pop_namespace ();
push_lang_context (lang_name_c);
set_exception_lang_code (EH_LANG_C_plus_plus);
set_exception_version_code (1);
CatchMatch
= builtin_function (flag_rtti
? "__throw_type_match_rtti"
: "__throw_type_match",
build_function_type (ptr_type_node,
tree_cons (NULL_TREE, const_ptr_type_node,
tree_cons (NULL_TREE, const_ptr_type_node,
tree_cons (NULL_TREE, ptr_type_node,
void_list_node)))),
NOT_BUILT_IN, NULL_PTR);
FirstExceptionMatch
= builtin_function ("__find_first_exception_table_match",
build_function_type (ptr_type_node,
tree_cons (NULL_TREE, ptr_type_node,
void_list_node)),
NOT_BUILT_IN, NULL_PTR);
Unwind
= builtin_function ("__unwind_function",
build_function_type (void_type_node,
tree_cons (NULL_TREE, ptr_type_node,
void_list_node)),
NOT_BUILT_IN, NULL_PTR);
pop_lang_context ();
protect_cleanup_actions_with_terminate = 1;
}
static tree
call_eh_info ()
{
tree fn;
fn = get_identifier ("__start_cp_handler");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
tree t1, t, fields[7];
push_obstacks_nochange ();
end_temporary_allocation ();
t1= make_lang_type (RECORD_TYPE);
fields[0] = build_lang_field_decl (FIELD_DECL,
get_identifier ("handler_label"), ptr_type_node);
fields[1] = build_lang_field_decl (FIELD_DECL,
get_identifier ("dynamic_handler_chain"), ptr_type_node);
fields[2] = build_lang_field_decl (FIELD_DECL,
get_identifier ("info"), ptr_type_node);
fields[3] = build_lang_field_decl (FIELD_DECL,
get_identifier ("table_index"), ptr_type_node);
finish_builtin_type (t1, "eh_context", fields, 3, ptr_type_node);
t1 = build_pointer_type (t1);
t1= make_lang_type (RECORD_TYPE);
fields[0] = build_lang_field_decl (FIELD_DECL,
get_identifier ("match_function"), ptr_type_node);
fields[1] = build_lang_field_decl (FIELD_DECL,
get_identifier ("language"), short_integer_type_node);
fields[2] = build_lang_field_decl (FIELD_DECL,
get_identifier ("version"), short_integer_type_node);
finish_builtin_type (t1, "__eh_info", fields, 2, ptr_type_node);
t = make_lang_type (RECORD_TYPE);
fields[0] = build_lang_field_decl (FIELD_DECL,
get_identifier ("eh_info"), t1);
fields[1] = build_lang_field_decl (FIELD_DECL, get_identifier ("value"),
ptr_type_node);
fields[2] = build_lang_field_decl (FIELD_DECL, get_identifier ("type"),
ptr_type_node);
fields[3] = build_lang_field_decl
(FIELD_DECL, get_identifier ("cleanup"),
build_pointer_type (build_function_type
(ptr_type_node, tree_cons
(NULL_TREE, ptr_type_node, void_list_node))));
fields[4] = build_lang_field_decl (FIELD_DECL, get_identifier ("caught"),
boolean_type_node);
fields[5] = build_lang_field_decl (FIELD_DECL, get_identifier ("next"),
build_pointer_type (t));
fields[6] = build_lang_field_decl
(FIELD_DECL, get_identifier ("handlers"), long_integer_type_node);
finish_builtin_type (t, "cp_eh_info", fields, 6, ptr_type_node);
t = build_pointer_type (t);
fn = build_lang_decl (FUNCTION_DECL, fn,
build_function_type (t, void_list_node));
DECL_EXTERNAL (fn) = 1;
TREE_PUBLIC (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
pushdecl_top_level (fn);
make_function_rtl (fn);
pop_obstacks ();
}
mark_used (fn);
return build_function_call (fn, NULL_TREE);
}
static void
push_eh_info ()
{
tree decl, fn = call_eh_info ();
decl = build_decl (VAR_DECL, get_identifier ("__exception_info"),
TREE_TYPE (fn));
DECL_ARTIFICIAL (decl) = 1;
DECL_INITIAL (decl) = fn;
decl = pushdecl (decl);
cp_finish_decl (decl, fn, NULL_TREE, 0, 0);
}
static tree
get_eh_info ()
{
tree t = lookup_name (get_identifier ("__exception_info"), 0);
return build_indirect_ref (t, NULL_PTR);
}
static tree
get_eh_value ()
{
return build_component_ref (get_eh_info (), get_identifier ("value"),
NULL_TREE, 0);
}
#if 0
static tree
get_eh_type ()
{
return build_component_ref (get_eh_info (), get_identifier ("type"),
NULL_TREE, 0);
}
static tree
get_eh_caught ()
{
return build_component_ref (get_eh_info (), get_identifier ("caught"),
NULL_TREE, 0);
}
static tree
get_eh_handlers ()
{
return build_component_ref (get_eh_info (), get_identifier ("handlers"),
NULL_TREE, 0);
}
#endif
static tree
build_eh_type_type (type)
tree type;
{
const char *typestring;
tree exp;
if (type == error_mark_node)
return error_mark_node;
if (TREE_CODE (type) == REFERENCE_TYPE)
type = TREE_TYPE (type);
type = TYPE_MAIN_VARIANT (type);
if (flag_rtti)
return build1 (ADDR_EXPR, ptr_type_node, get_typeid_1 (type));
typestring = build_overload_name (type, 1, 1);
exp = combine_strings (build_string (strlen (typestring)+1, typestring));
return build1 (ADDR_EXPR, ptr_type_node, exp);
}
static tree
build_eh_type_type_ref (type)
tree type;
{
const char *typestring;
tree exp;
if (type == error_mark_node)
return error_mark_node;
if (TREE_CODE (type) == REFERENCE_TYPE)
type = TREE_TYPE (type);
type = TYPE_MAIN_VARIANT (type);
push_obstacks_nochange ();
end_temporary_allocation ();
if (flag_rtti)
{
exp = get_tinfo_fn (type);
TREE_USED (exp) = 1;
mark_inline_for_output (exp);
exp = build1 (ADDR_EXPR, ptr_type_node, exp);
}
else
{
typestring = build_overload_name (type, 1, 1);
exp = combine_strings (build_string (strlen (typestring)+1, typestring));
exp = build1 (ADDR_EXPR, ptr_type_node, exp);
}
pop_obstacks ();
return (exp);
}
static tree
build_eh_type (exp)
tree exp;
{
if (flag_rtti)
{
exp = build_typeid (exp);
return build1 (ADDR_EXPR, ptr_type_node, exp);
}
return build_eh_type_type (TREE_TYPE (exp));
}
void
mark_all_runtime_matches ()
{
int x,num;
void **ptr;
tree exp;
num = find_all_handler_type_matches (&ptr);
if (num == 0 || ptr == NULL)
return;
for (x=0; x <num; x++)
{
exp = (tree) ptr[x];
if (TREE_CODE (exp) == ADDR_EXPR)
{
exp = TREE_OPERAND (exp, 0);
if (TREE_CODE (exp) == FUNCTION_DECL)
TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (exp)) = 1;
}
}
free (ptr);
}
static tree
do_pop_exception ()
{
tree fn, cleanup;
fn = get_identifier ("__cp_pop_exception");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
push_obstacks_nochange ();
end_temporary_allocation ();
fn = build_lang_decl
(FUNCTION_DECL, fn,
build_function_type (void_type_node, tree_cons
(NULL_TREE, ptr_type_node, void_list_node)));
DECL_EXTERNAL (fn) = 1;
TREE_PUBLIC (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
pushdecl_top_level (fn);
make_function_rtl (fn);
pop_obstacks ();
}
mark_used (fn);
cleanup = lookup_name (get_identifier ("__exception_info"), 0);
cleanup = build_function_call (fn, expr_tree_cons
(NULL_TREE, cleanup, NULL_TREE));
return cleanup;
}
static void
push_eh_cleanup ()
{
int yes;
yes = suspend_momentary ();
expand_decl_cleanup (NULL_TREE, do_pop_exception ());
resume_momentary (yes);
}
static tree
build_terminate_handler ()
{
int yes = suspend_momentary ();
tree term = build_function_call (Terminate, NULL_TREE);
resume_momentary (yes);
return term;
}
void
expand_start_catch_block (declspecs, declarator)
tree declspecs, declarator;
{
tree decl;
if (processing_template_decl)
{
if (declspecs)
{
decl = grokdeclarator (declarator, declspecs, CATCHPARM,
1, NULL_TREE);
pushdecl (decl);
decl = build_min_nt (DECL_STMT, copy_to_permanent (declarator),
copy_to_permanent (declspecs),
NULL_TREE);
add_tree (decl);
}
return;
}
if (! doing_eh (1))
return;
process_start_catch_block (declspecs, declarator);
}
static void
process_start_catch_block (declspecs, declarator)
tree declspecs, declarator;
{
tree decl = NULL_TREE;
tree init;
pushlevel (0);
expand_start_bindings (0);
if (declspecs)
{
decl = grokdeclarator (declarator, declspecs, CATCHPARM, 1, NULL_TREE);
if (decl == NULL_TREE)
error ("invalid catch parameter");
}
if (decl)
start_catch_handler (build_eh_type_type_ref (TREE_TYPE (decl)));
else
start_catch_handler (CATCH_ALL_TYPE);
emit_line_note (input_filename, lineno);
push_eh_info ();
if (decl)
{
tree exp;
tree init_type;
TREE_USED (decl) = 1;
init_type = TREE_TYPE (decl);
if (TREE_CODE (init_type) != REFERENCE_TYPE
&& TREE_CODE (init_type) != POINTER_TYPE)
init_type = build_reference_type (init_type);
exp = get_eh_value ();
if (TREE_CODE (init_type) == REFERENCE_TYPE
&& TREE_CODE (TREE_TYPE (init_type)) == POINTER_TYPE)
exp = build_unary_op (ADDR_EXPR, exp, 1);
exp = ocp_convert (init_type , exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
push_eh_cleanup ();
pushlevel (0);
expand_start_bindings (0);
init = convert_from_reference (exp);
if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
{
init = ocp_convert (TREE_TYPE (decl), init,
CONV_IMPLICIT|CONV_FORCE_TEMP, 0);
init = build (TRY_CATCH_EXPR, TREE_TYPE (init), init,
build_terminate_handler ());
}
DECL_INITIAL (decl) = init;
decl = pushdecl (decl);
start_decl_1 (decl);
cp_finish_decl (decl, init, NULL_TREE, 0,
LOOKUP_ONLYCONVERTING|DIRECT_BIND);
}
else
{
push_eh_cleanup ();
pushlevel (0);
expand_start_bindings (0);
}
emit_line_note (input_filename, lineno);
}
void
expand_end_catch_block ()
{
if (! doing_eh (1))
return;
expand_end_bindings (getdecls (), kept_level_p (), 0);
poplevel (kept_level_p (), 1, 0);
expand_end_bindings (getdecls (), kept_level_p (), 0);
poplevel (kept_level_p (), 1, 0);
expand_goto (top_label_entry (&caught_return_label_stack));
end_catch_handler ();
}
void
expand_start_eh_spec ()
{
expand_start_try_stmts ();
}
static void
expand_end_eh_spec (raises)
tree raises;
{
tree tmp, fn, decl, types = NULL_TREE;
int count = 0;
expand_start_all_catch ();
expand_start_catch_block (NULL_TREE, NULL_TREE);
for (; raises && TREE_VALUE (raises); raises = TREE_CHAIN (raises))
{
types = expr_tree_cons
(NULL_TREE, build_eh_type_type (TREE_VALUE (raises)), types);
++count;
}
types = build_nt (CONSTRUCTOR, NULL_TREE, types);
TREE_HAS_CONSTRUCTOR (types) = 1;
tmp = build_cplus_array_type (const_ptr_type_node, NULL_TREE);
decl = build_decl (VAR_DECL, NULL_TREE, tmp);
DECL_ARTIFICIAL (decl) = 1;
DECL_INITIAL (decl) = types;
cp_finish_decl (decl, types, NULL_TREE, 0, 0);
decl = decay_conversion (decl);
fn = get_identifier ("__check_eh_spec");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
push_obstacks_nochange ();
end_temporary_allocation ();
tmp = tree_cons
(NULL_TREE, integer_type_node, tree_cons
(NULL_TREE, TREE_TYPE (decl), void_list_node));
tmp = build_function_type (void_type_node, tmp);
fn = build_lang_decl (FUNCTION_DECL, fn, tmp);
DECL_EXTERNAL (fn) = 1;
TREE_PUBLIC (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
TREE_THIS_VOLATILE (fn) = 1;
pushdecl_top_level (fn);
make_function_rtl (fn);
pop_obstacks ();
}
mark_used (fn);
tmp = expr_tree_cons (NULL_TREE, build_int_2 (count, 0), expr_tree_cons
(NULL_TREE, decl, NULL_TREE));
tmp = build_call (fn, TREE_TYPE (TREE_TYPE (fn)), tmp);
expand_expr (tmp, const0_rtx, VOIDmode, EXPAND_NORMAL);
expand_end_catch_block ();
expand_end_all_catch ();
}
void
expand_exception_blocks ()
{
do_pending_stack_adjust ();
push_to_sequence (catch_clauses);
expand_leftover_cleanups ();
do_pending_stack_adjust ();
catch_clauses = get_insns ();
end_sequence ();
if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl)))
{
expand_end_eh_spec (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl)));
do_pending_stack_adjust ();
push_to_sequence (catch_clauses);
expand_leftover_cleanups ();
do_pending_stack_adjust ();
catch_clauses = get_insns ();
end_sequence ();
}
if (catch_clauses)
{
rtx funcend = gen_label_rtx ();
emit_jump (funcend);
if (exceptions_via_longjmp == 0)
expand_eh_region_start ();
emit_insns (catch_clauses);
catch_clauses = NULL_RTX;
if (exceptions_via_longjmp == 0)
expand_eh_region_end (build_terminate_handler ());
expand_leftover_cleanups ();
emit_label (funcend);
}
}
tree
start_anon_func ()
{
static int counter = 0;
int old_interface_unknown = interface_unknown;
char name[32];
tree params;
tree t;
push_cp_function_context (NULL_TREE);
push_to_top_level ();
push_lang_context (lang_name_c);
interface_unknown = 1;
params = void_list_node;
sprintf (name, "__tcf_%d", counter++);
t = make_call_declarator (get_identifier (name), params, NULL_TREE,
NULL_TREE);
start_function (decl_tree_cons (NULL_TREE, get_identifier ("static"),
void_list_node),
t, NULL_TREE, 0);
store_parm_decls ();
pushlevel (0);
clear_last_expr ();
push_momentary ();
expand_start_bindings (0);
emit_line_note (input_filename, lineno);
interface_unknown = old_interface_unknown;
pop_lang_context ();
return current_function_decl;
}
void
end_anon_func ()
{
expand_end_bindings (getdecls (), 1, 0);
poplevel (1, 0, 0);
pop_momentary ();
finish_function (lineno, 0, 0);
pop_from_top_level ();
pop_cp_function_context (NULL_TREE);
}
static tree
alloc_eh_object (type)
tree type;
{
tree fn, exp;
fn = get_identifier ("__eh_alloc");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
tree tmp;
push_obstacks_nochange ();
end_temporary_allocation ();
tmp = tree_cons (NULL_TREE, sizetype, void_list_node);
fn = build_lang_decl (FUNCTION_DECL, fn,
build_function_type (ptr_type_node, tmp));
DECL_EXTERNAL (fn) = 1;
TREE_PUBLIC (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
pushdecl_top_level (fn);
make_function_rtl (fn);
pop_obstacks ();
}
mark_used (fn);
exp = build_function_call (fn, expr_tree_cons
(NULL_TREE, size_in_bytes (type), NULL_TREE));
exp = build1 (NOP_EXPR, build_pointer_type (type), exp);
return exp;
}
void
expand_throw (exp)
tree exp;
{
tree fn;
static tree cleanup_type;
if (! doing_eh (1))
return;
if (exp)
{
tree throw_type;
tree cleanup = NULL_TREE, e;
exp = decay_conversion (exp);
if (cleanup_type == NULL_TREE)
{
push_obstacks_nochange ();
end_temporary_allocation ();
cleanup_type = build_pointer_type
(build_function_type
(void_type_node, tree_cons
(NULL_TREE, ptr_type_node, tree_cons
(NULL_TREE, integer_type_node, void_list_node))));
pop_obstacks ();
}
if (TYPE_PTR_P (TREE_TYPE (exp)))
throw_type = build_eh_type (exp);
else
{
tree object, ptr;
expand_start_target_temps ();
#if 0
preexpand_calls (exp);
#else
if (TREE_SIDE_EFFECTS (exp))
{
tree temp = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp));
DECL_ARTIFICIAL (temp) = 1;
DECL_RTL (temp) = assign_temp (TREE_TYPE (exp), 2, 0, 1);
DECL_INITIAL (temp) = exp;
cp_finish_decl (temp, exp, NULL_TREE, 0, LOOKUP_ONLYCONVERTING);
exp = temp;
}
#endif
ptr = save_expr (alloc_eh_object (TREE_TYPE (exp)));
expand_expr (ptr, const0_rtx, VOIDmode, 0);
expand_eh_region_start ();
object = build_indirect_ref (ptr, NULL_PTR);
exp = build_modify_expr (object, INIT_EXPR, exp);
if (exp == error_mark_node)
error (" in thrown expression");
expand_expr (exp, const0_rtx, VOIDmode, 0);
expand_eh_region_end (build_terminate_handler ());
expand_end_target_temps ();
throw_type = build_eh_type (object);
if (TYPE_HAS_DESTRUCTOR (TREE_TYPE (object)))
{
cleanup = lookup_fnfields (TYPE_BINFO (TREE_TYPE (object)),
dtor_identifier, 0);
cleanup = TREE_VALUE (cleanup);
mark_used (cleanup);
mark_addressable (cleanup);
cleanup = build1 (ADDR_EXPR, cleanup_type, cleanup);
}
exp = ptr;
}
exp = convert (ptr_type_node, exp);
if (cleanup == NULL_TREE)
{
cleanup = build_int_2 (0, 0);
TREE_TYPE (cleanup) = cleanup_type;
}
fn = get_identifier ("__cp_push_exception");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
tree tmp;
push_obstacks_nochange ();
end_temporary_allocation ();
tmp = tree_cons
(NULL_TREE, ptr_type_node, tree_cons
(NULL_TREE, ptr_type_node, tree_cons
(NULL_TREE, cleanup_type, void_list_node)));
fn = build_lang_decl (FUNCTION_DECL, fn,
build_function_type (void_type_node, tmp));
DECL_EXTERNAL (fn) = 1;
TREE_PUBLIC (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
pushdecl_top_level (fn);
make_function_rtl (fn);
pop_obstacks ();
}
mark_used (fn);
e = expr_tree_cons (NULL_TREE, exp, expr_tree_cons
(NULL_TREE, throw_type, expr_tree_cons
(NULL_TREE, cleanup, NULL_TREE)));
e = build_function_call (fn, e);
expand_expr (e, const0_rtx, VOIDmode, 0);
}
else
{
tree fn = get_identifier ("__uncatch_exception");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
push_obstacks_nochange ();
end_temporary_allocation ();
fn = build_lang_decl (FUNCTION_DECL, fn,
build_function_type (void_type_node,
void_list_node));
DECL_EXTERNAL (fn) = 1;
TREE_PUBLIC (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
pushdecl_top_level (fn);
make_function_rtl (fn);
pop_obstacks ();
}
mark_used (fn);
exp = build_function_call (fn, NULL_TREE);
expand_expr (exp, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
expand_internal_throw ();
}
tree
build_throw (e)
tree e;
{
if (e == error_mark_node)
return e;
if (processing_template_decl)
return build_min (THROW_EXPR, void_type_node, e);
if (e == null_node)
cp_warning ("throwing NULL, which has integral, not pointer type");
e = build1 (THROW_EXPR, void_type_node, e);
TREE_SIDE_EFFECTS (e) = 1;
TREE_USED (e) = 1;
return e;
}