[multiple changes]

Fri Oct 31 01:45:31 1997  Jason Merrill  <jason@yorick.cygnus.com>

	* libgcc2.c (L_eh): Define __eh_pc.
	Replace __eh_type with generic pointer __eh_info.

Fri Oct 31 01:47:57 1997  Jason Merrill  <jason@yorick.cygnus.com>

	Support for nested exceptions.
	* tinfo2.cc (__is_pointer): New fn.
	* exception.cc (struct cp_eh_info): Define.
	(__cp_exception_info, __uncatch_exception): New fns.
	(__cp_push_exception, __cp_pop_exception): New fns.
	* except.c: Lose saved_throw_{type,value,cleanup,in_catch}.
 	Lose empty_fndecl.
	(init_exception_processing): Likewise.  __eh_pc is now external.
	(push_eh_info): New fn.
	(get_eh_{info,value,type,caught}): New fns.
	(push_eh_cleanup): Just call __cp_pop_exception.
	(expand_start_catch_block): Use push_eh_info.  Start the eh region
	sooner.
	(expand_end_eh_spec): Use push_eh_info.
	(expand_throw): Call __cp_push_exception to set up the exception info.
	Just pass the destructor or 0 as the cleanup.  Call __uncatch_exception
	when we rethrow.
	(expand_builtin_throw): Don't refer to empty_fndecl.

From-SVN: r16248
This commit is contained in:
Jason Merrill 1997-10-31 09:52:55 +00:00 committed by Jason Merrill
parent 59fe8c2c20
commit 6874c2647b
6 changed files with 365 additions and 117 deletions

View File

@ -1,3 +1,8 @@
Fri Oct 31 01:45:31 1997 Jason Merrill <jason@yorick.cygnus.com>
* libgcc2.c (L_eh): Define __eh_pc.
Replace __eh_type with generic pointer __eh_info.
Fri Oct 31 00:34:55 1996 J"orn Rennecke <amylaar@cygnus.co.uk>
* expr.c (expand_increment): When enqueing a postincrement for a MEM,

View File

@ -1,3 +1,24 @@
Fri Oct 31 01:47:57 1997 Jason Merrill <jason@yorick.cygnus.com>
Support for nested exceptions.
* tinfo2.cc (__is_pointer): New fn.
* exception.cc (struct cp_eh_info): Define.
(__cp_exception_info, __uncatch_exception): New fns.
(__cp_push_exception, __cp_pop_exception): New fns.
* except.c: Lose saved_throw_{type,value,cleanup,in_catch}.
Lose empty_fndecl.
(init_exception_processing): Likewise. __eh_pc is now external.
(push_eh_info): New fn.
(get_eh_{info,value,type,caught}): New fns.
(push_eh_cleanup): Just call __cp_pop_exception.
(expand_start_catch_block): Use push_eh_info. Start the eh region
sooner.
(expand_end_eh_spec): Use push_eh_info.
(expand_throw): Call __cp_push_exception to set up the exception info.
Just pass the destructor or 0 as the cleanup. Call __uncatch_exception
when we rethrow.
(expand_builtin_throw): Don't refer to empty_fndecl.
Thu Oct 23 02:01:30 1997 Jason Merrill <jason@yorick.cygnus.com>
* pt.c (instantiate_decl): SET_DECL_IMPLICIT_INSTANTIATION on new decl.

View File

@ -180,8 +180,6 @@ static tree Unwind;
/* Holds a ready to emit call to "terminate". */
static tree TerminateFunctionCall;
static tree empty_fndecl;
/* ====================================================================== */
@ -196,14 +194,6 @@ static tree empty_fndecl;
/* Holds the pc for doing "throw" */
static tree saved_pc;
/* Holds the type of the thing being thrown. */
static tree saved_throw_type;
/* Holds the value being thrown. */
static tree saved_throw_value;
/* Holds the cleanup for the value being thrown. */
static tree saved_cleanup;
/* Indicates if we are in a catch clause. */
static tree saved_in_catch;
extern int throw_used;
extern rtx catch_clauses;
@ -290,12 +280,6 @@ init_exception_processing ()
tree_cons (NULL_TREE, ptr_type_node,
void_list_node)),
NOT_BUILT_IN, NULL_PTR);
empty_fndecl
= builtin_function ("__empty",
vtype,
NOT_BUILT_IN, NULL_PTR);
DECL_EXTERNAL (empty_fndecl) = 1;
TREE_PUBLIC (empty_fndecl) = 1;
Unexpected = default_conversion (unexpected_fndecl);
Terminate = default_conversion (terminate_fndecl);
@ -310,47 +294,118 @@ init_exception_processing ()
pop_lang_context ();
declspecs = tree_cons (NULL_TREE, get_identifier ("void"), NULL_TREE);
d = build_parse_node (INDIRECT_REF, get_identifier ("__eh_pc"));
d = start_decl (d, declspecs, 0);
DECL_COMMON (d) = 1;
cp_finish_decl (d, NULL_TREE, NULL_TREE, 1, 0);
saved_pc = lookup_name (get_identifier ("__eh_pc"), 0);
declspecs = tree_cons (NULL_TREE, get_identifier ("void"), NULL_TREE);
d = build_parse_node (INDIRECT_REF, get_identifier ("__eh_type"));
d = start_decl (d, declspecs, 0);
DECL_COMMON (d) = 1;
cp_finish_decl (d, NULL_TREE, NULL_TREE, 1, 0);
saved_throw_type = lookup_name (get_identifier ("__eh_type"), 0);
declspecs = tree_cons (NULL_TREE, get_identifier ("void"), NULL_TREE);
d = build_parse_node (INDIRECT_REF, get_identifier ("__eh_value"));
d = start_decl (d, declspecs, 0);
DECL_COMMON (d) = 1;
cp_finish_decl (d, NULL_TREE, NULL_TREE, 1, 0);
saved_throw_value = lookup_name (get_identifier ("__eh_value"), 0);
declspecs = tree_cons (NULL_TREE, get_identifier ("void"), NULL_TREE);
d = build_parse_node (INDIRECT_REF, get_identifier ("__eh_cleanup"));
d = make_call_declarator (d, void_list_node, NULL_TREE, NULL_TREE);
d = start_decl (d, declspecs, 0);
DECL_COMMON (d) = 1;
cp_finish_decl (d, NULL_TREE, NULL_TREE, 1, 0);
saved_cleanup = lookup_name (get_identifier ("__eh_cleanup"), 0);
declspecs = tree_cons (NULL_TREE, get_identifier ("bool"), NULL_TREE);
d = get_identifier ("__eh_in_catch");
d = start_decl (d, declspecs, 0);
DECL_COMMON (d) = 1;
cp_finish_decl (d, NULL_TREE, NULL_TREE, 1, 0);
saved_in_catch = lookup_name (get_identifier ("__eh_in_catch"), 0);
d = build_decl (VAR_DECL, get_identifier ("__eh_pc"), ptr_type_node);
TREE_PUBLIC (d) = 1;
DECL_EXTERNAL (d) = 1;
DECL_ARTIFICIAL (d) = 1;
cp_finish_decl (d, NULL_TREE, NULL_TREE, 0, 0);
saved_pc = d;
/* If we use setjmp/longjmp EH, arrange for all cleanup actions to
be protected with __terminate. */
protect_cleanup_actions_with_terminate = 1;
}
/* Retrieve a pointer to the cp_eh_info node for the current exception
and save it in the current binding level. */
static void
push_eh_info ()
{
tree decl, fn;
fn = get_identifier ("__cp_exception_info");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
tree t, fields[5];
/* Declare cp_eh_info * __cp_exception_info (void),
as defined in exception.cc. */
push_obstacks_nochange ();
end_temporary_allocation ();
/* struct cp_eh_info. This must match exception.cc. Note that this
type is not pushed anywhere. */
t = make_lang_type (RECORD_TYPE);
fields[0] = build_lang_field_decl (FIELD_DECL, get_identifier ("value"),
ptr_type_node);
fields[1] = build_lang_field_decl (FIELD_DECL, get_identifier ("type"),
ptr_type_node);
fields[2] = 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[3] = build_lang_field_decl (FIELD_DECL, get_identifier ("caught"),
boolean_type_node);
fields[4] = build_lang_field_decl (FIELD_DECL, get_identifier ("next"),
build_pointer_type (t));
finish_builtin_type (t, "cp_eh_info", fields, 5, ptr_type_node);
t = build_pointer_type (t);
/* And now the function. */
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);
assemble_external (fn);
pop_obstacks ();
}
fn = build_function_call (fn, NULL_TREE);
/* Remember the pointer to the current exception info; it won't change
during this catch block. */
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);
}
/* Returns a reference to the cp_eh_info node for the current exception. */
static tree
get_eh_info ()
{
/* Look for the pointer pushed in push_eh_info. */
tree t = lookup_name (get_identifier ("__exception_info"), 0);
return build_indirect_ref (t, NULL_PTR);
}
/* Returns a reference to the current exception object. */
static tree
get_eh_value ()
{
return build_component_ref (get_eh_info (), get_identifier ("value"),
NULL_TREE, 0);
}
/* Returns a reference to the current exception type. */
static tree
get_eh_type ()
{
return build_component_ref (get_eh_info (), get_identifier ("type"),
NULL_TREE, 0);
}
/* Returns a reference to whether or not the current exception
has been caught. */
static tree
get_eh_caught ()
{
return build_component_ref (get_eh_info (), get_identifier ("caught"),
NULL_TREE, 0);
}
/* Build a type value for use at runtime for a type that is matched
against by the exception handling system. */
@ -396,19 +451,37 @@ build_eh_type (exp)
return build_eh_type_type (TREE_TYPE (exp));
}
/* This routine creates the cleanup for the exception handling object. */
/* This routine creates the cleanup for the current exception. */
static void
push_eh_cleanup ()
{
/* All cleanups must last longer than normal. */
int yes = suspend_momentary ();
tree fn, cleanup;
fn = get_identifier ("__cp_pop_exception");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
/* Declare void __cp_pop_exception (void), as defined in exception.cc. */
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);
assemble_external (fn);
pop_obstacks ();
}
/* Arrange to do a dynamically scoped cleanup upon exit from this region. */
tree cleanup = build_function_call (saved_cleanup, NULL_TREE);
cleanup = build (COMPOUND_EXPR, void_type_node, cleanup,
build_modify_expr (saved_in_catch, NOP_EXPR,
build_modify_expr (saved_throw_type, NOP_EXPR, integer_zero_node)));
cleanup = build_function_call (fn, NULL_TREE);
expand_decl_cleanup (NULL_TREE, cleanup);
resume_momentary (yes);
@ -456,6 +529,23 @@ expand_start_catch_block (declspecs, declarator)
emit_line_note (input_filename, lineno);
push_eh_info ();
/* If we are not doing setjmp/longjmp EH, because we are reordered
out of line, we arrange to rethrow in the outer context so as to
skip through the terminate region we are nested in, should we
encounter an exception in the catch handler.
If we are doing setjmp/longjmp EH, we need to skip through the EH
object cleanup region. This isn't quite right, as we really need
to clean the object up, but we cannot do that until we track
multiple EH objects.
Matches the end in expand_end_catch_block. */
expand_eh_region_start ();
push_eh_cleanup ();
if (declspecs)
{
tree exp;
@ -467,12 +557,6 @@ expand_start_catch_block (declspecs, declarator)
if (decl == NULL_TREE)
{
error ("invalid catch parameter");
/* This is cheap, but we want to maintain the data
structures. */
expand_eh_region_start ();
return;
}
@ -486,11 +570,11 @@ expand_start_catch_block (declspecs, declarator)
&& TREE_CODE (init_type) != POINTER_TYPE)
init_type = build_reference_type (init_type);
exp = saved_throw_value;
exp = get_eh_value ();
exp = expr_tree_cons (NULL_TREE,
build_eh_type_type (TREE_TYPE (decl)),
expr_tree_cons (NULL_TREE,
saved_throw_type,
get_eh_type (),
expr_tree_cons (NULL_TREE, exp, NULL_TREE)));
exp = build_function_call (CatchMatch, exp);
call_rtx = expand_call (exp, NULL_RTX, 0);
@ -505,8 +589,6 @@ expand_start_catch_block (declspecs, declarator)
/* if it returned FALSE, jump over the catch block, else fall into it */
emit_jump_insn (gen_beq (false_label_rtx));
push_eh_cleanup ();
init = convert_from_reference (save_expr (make_tree (init_type, call_rtx)));
/* Do we need the below two lines? */
@ -515,27 +597,9 @@ expand_start_catch_block (declspecs, declarator)
decl = pushdecl (decl);
cp_finish_decl (decl, init, NULL_TREE, 0, LOOKUP_ONLYCONVERTING);
}
else
{
push_eh_cleanup ();
/* Fall into the catch all section. */
}
emit_move_insn (DECL_RTL (saved_in_catch), const1_rtx);
/* If we are not doing setjmp/longjmp EH, because we are reordered
out of line, we arrange to rethrow in the outer context so as to
skip through the terminate region we are nested in, should we
encounter an exception in the catch handler.
If we are doing setjmp/longjmp EH, we need to skip through the EH
object cleanup region. This isn't quite right, as we really need
to clean the object up, but we cannot do that until we track
multiple EH objects.
Matches the end in expand_end_catch_block. */
expand_eh_region_start ();
init = build_modify_expr (get_eh_caught (), NOP_EXPR, integer_one_node);
expand_expr (init, const0_rtx, VOIDmode, EXPAND_NORMAL);
emit_line_note (input_filename, lineno);
}
@ -845,7 +909,8 @@ expand_builtin_throw ()
#ifdef DONT_ACCESS_GBLS_AFTER_EPILOGUE
if (DONT_ACCESS_GBLS_AFTER_EPILOGUE)
{
t = make_tree (build_pointer_type (TREE_TYPE (empty_fndecl)),
t = build_function_type (void_type_node, void_list_node);
t = make_tree (build_pointer_type (t),
hard_function_value (ptr_type_node,
NULL_TREE));
t = build_function_call (t, NULL_TREE);
@ -965,6 +1030,9 @@ expand_end_eh_spec (raises)
emit_label (check);
emit_move_insn (flag, const1_rtx);
cont = gen_label_rtx ();
push_eh_info ();
while (raises)
{
tree exp;
@ -973,11 +1041,11 @@ expand_end_eh_spec (raises)
if (match_type)
{
/* check TREE_VALUE (raises) here */
exp = saved_throw_value;
exp = get_eh_value ();
exp = expr_tree_cons (NULL_TREE,
build_eh_type_type (match_type),
expr_tree_cons (NULL_TREE,
saved_throw_type,
get_eh_type (),
expr_tree_cons (NULL_TREE, exp, NULL_TREE)));
exp = build_function_call (CatchMatch, exp);
assemble_external (TREE_OPERAND (CatchMatch, 0));
@ -1123,6 +1191,8 @@ expand_throw (exp)
tree exp;
{
rtx label;
tree fn;
static tree cleanup_type;
if (! doing_eh (1))
return;
@ -1130,12 +1200,26 @@ expand_throw (exp)
if (exp)
{
tree throw_type;
tree cleanup = empty_fndecl, e;
tree cleanup = NULL_TREE, e;
/* throw expression */
/* First, decay it. */
exp = decay_conversion (exp);
/* cleanup_type is void (*)(void *, int),
the internal type of a destructor. */
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 (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
{
throw_type = build_eh_type (exp);
@ -1156,33 +1240,83 @@ expand_throw (exp)
object = build_indirect_ref (exp, NULL_PTR);
throw_type = build_eh_type (object);
/* Build __tcf_ function. */
cleanup = start_anon_func ();
object = build_delete (TREE_TYPE (exp), saved_throw_value,
integer_three_node, LOOKUP_NORMAL|LOOKUP_DESTRUCTOR, 0);
expand_expr (object, const0_rtx, VOIDmode, 0);
end_anon_func ();
mark_addressable (cleanup);
if (TYPE_HAS_DESTRUCTOR (TREE_TYPE (object)))
{
cleanup = lookup_fnfields (TYPE_BINFO (TREE_TYPE (object)),
dtor_identifier, 0);
cleanup = TREE_VALUE (cleanup);
mark_addressable (cleanup);
/* Pretend it's a normal function. */
cleanup = build1 (ADDR_EXPR, cleanup_type, cleanup);
}
}
if (cleanup == empty_fndecl)
assemble_external (empty_fndecl);
e = build_modify_expr (saved_throw_type, NOP_EXPR, throw_type);
expand_expr (e, const0_rtx, VOIDmode, 0);
if (cleanup == NULL_TREE)
{
cleanup = build_int_2 (0, 0);
TREE_TYPE (cleanup) = cleanup_type;
}
e = build_modify_expr (saved_throw_value, NOP_EXPR, exp);
e = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (e), e);
expand_expr (e, const0_rtx, VOIDmode, 0);
fn = get_identifier ("__cp_push_exception");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
/* Declare __cp_push_exception (void*, void*, void (*)(void*, int)),
as defined in exception.cc. */
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);
assemble_external (fn);
pop_obstacks ();
}
cleanup = build_unary_op (ADDR_EXPR, cleanup, 0);
cleanup = build_modify_expr (saved_cleanup, NOP_EXPR, cleanup);
expand_expr (cleanup, const0_rtx, VOIDmode, 0);
/* The throw expression is a full-expression. */
exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp);
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
{
/* rethrow current exception */
/* This part is easy, as we don't have to do anything else. */
/* rethrow current exception; note that it's no longer caught. */
tree fn = get_identifier ("__uncatch_exception");
if (IDENTIFIER_GLOBAL_VALUE (fn))
fn = IDENTIFIER_GLOBAL_VALUE (fn);
else
{
/* Declare void __uncatch_exception (void)
as defined in exception.cc. */
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);
assemble_external (fn);
pop_obstacks ();
}
exp = build_function_call (fn, NULL_TREE);
expand_expr (exp, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
if (exceptions_via_longjmp)

View File

@ -73,6 +73,79 @@ unexpected ()
__unexpected_func ();
}
/* C++-specific state about the current exception.
This must match init_exception_processing(). */
struct cp_eh_info
{
void *value;
void *type;
void (*cleanup)(void *, int);
bool caught;
cp_eh_info *next;
};
/* Language-specific EH info pointer, defined in libgcc2. */
extern cp_eh_info *__eh_info; // actually void*
/* Is P the type_info node for a pointer of some kind? */
extern bool __is_pointer (void *);
/* Compiler hook to return a pointer to the info for the current exception.
Used by get_eh_info (). */
extern "C" cp_eh_info *
__cp_exception_info (void)
{
return __eh_info;
}
/* Compiler hook to push a new exception onto the stack.
Used by expand_throw(). */
extern "C" void
__cp_push_exception (void *value, void *type, void (*cleanup)(void *, int))
{
cp_eh_info *p = new cp_eh_info;
p->value = value;
p->type = type;
p->cleanup = cleanup;
p->caught = false;
p->next = __eh_info;
__eh_info = p;
}
/* Compiler hook to pop an exception that has been finalized. Used by
push_eh_cleanup(). */
extern "C" void
__cp_pop_exception (void)
{
cp_eh_info *p = __eh_info;
if (p->cleanup)
/* 3 is a magic value for destructors; see build_delete(). */
p->cleanup (p->value, 3);
else if (__is_pointer (p->type))
/* do nothing; pointers are passed directly in p->value. */;
else
delete p->value;
__eh_info = p->next;
delete p;
}
extern "C" void
__uncatch_exception (void)
{
cp_eh_info *p = __cp_exception_info ();
if (p)
p->caught = false;
/* otherwise __throw will call terminate(); don't crash here. */
}
extern "C" void
__throw_bad_cast (void)
{
@ -91,12 +164,13 @@ __throw_bad_exception (void)
throw bad_exception ();
}
/* Has the current exception been caught? */
bool
uncaught_exception ()
{
extern void *__eh_type;
extern bool __eh_in_catch;
return __eh_type && ! __eh_in_catch;
cp_eh_info *p = __cp_exception_info ();
return p && ! p->caught;
}
const char * exception::

View File

@ -258,6 +258,18 @@ __throw_type_match_rtti (void *catch_type_r, void *throw_type_r, void *objptr)
return new_objptr;
}
/* Called from __cp_pop_exception. Is P the type_info node for a pointer
of some kind? */
bool
__is_pointer (void *p)
{
const type_info *t = reinterpret_cast <const type_info *>(p);
const __pointer_type_info *pt =
dynamic_cast <const __pointer_type_info *> (t);
return pt != 0;
}
extern "C" void
__rtti_ptr (void *addr, const char *n, const type_info *ti)
{ new (addr) __pointer_type_info (n, *ti); }

View File

@ -3109,7 +3109,9 @@ int _exit_dummy_decl = 0; /* prevent compiler & linker warnings */
/* Shared exception handling support routines. */
extern void *__eh_type;
/* Language-specific information about the active exception(s). If there
are no active exceptions, it is set to 0. */
void *__eh_info;
void
__default_terminate ()
@ -3224,7 +3226,7 @@ __sjthrow ()
/* We must call terminate if we try and rethrow an exception, when
there is no exception currently active and when there are no
handlers left. */
if (! __eh_type || (*dhc) == top_elt)
if (! __eh_info || (*dhc) == top_elt)
__terminate ();
/* Find the jmpbuf associated with the top element of the dynamic
@ -3307,7 +3309,7 @@ __sjpopnthrow ()
/* This value identifies the place from which an exception is being
thrown. */
extern void *__eh_pc;
void *__eh_pc;
#ifdef EH_TABLE_LOOKUP
@ -3652,7 +3654,7 @@ __throw ()
/* This is required for C++ semantics. We must call terminate if we
try and rethrow an exception, when there is no exception currently
active. */
if (! __eh_type)
if (! __eh_info)
__terminate ();
/* Start at our stack frame. */