Skip to content

Commit 8754124

Browse files
author
aoliva
committed
rework Ada EH Machine_Occurrence deallocation
Introduce exception handler ABI #1 to ensure single release, no access after release of reraised Machine_Occurrences, and no failure to re-reraise a Machine_Occurrence. Unlike Ada exceptions, foreign exceptions do not get a new Machine_Occurrence upon reraise, but each handler would delete the exception upon completion, normal or exceptional, save for the case of a 'raise;' statement within the handler, that avoided the delete by clearing the exception pointer that the cleanup would use to release it. The cleared exception pointer might then be used by a subsequent reraise within the same handler. Get_Current_Excep.all would also expose the Machine_Occurrence to reuse by Reraise_Occurrence, even for native exceptions. Under ABI #1, Begin_Handler_v1 claims responsibility for releasing an exception by saving its cleanup and setting it to Claimed_Cleanup. End_Handler_v1 restores the cleanup and runs it, as long as it isn't still Claimed_Cleanup (which indicates an enclosing handler has already claimed responsibility for releasing it), and as long as the same exception is not being propagated up (the next handler of the propagating exception will then claim responsibility for releasing it), so reraise no longer needs to clear the exception pointer, and it can just propagate the exception, just like Reraise_Occurrence. ABI #1 is fully interoperable with ABI #0, i.e., exception handlers that call the #0 primitives can be linked together with ones that call the #1 primitives, and they will not misbehave. When a #1 handler claims responsibility for releasing an exception, even #0 reraises dynamically nested within it will refrain from releasing it. However, when a #0 handler is a handler of a foreign exception that would have been responsible for releasing it with #1, a Reraise_Occurrence of that foreign or other Machine_Occurrence-carrying exception may still cause the exception to be released multiple times, and to be used after it is first released, even if other handlers of the foreign exception use #1. for gcc/ada/ChangeLog * libgnat/a-exexpr.adb (Begin_Handler_v1, End_Handler_v1): New. (Claimed_Cleanup): New. (Begin_Handler, End_Handler): Document. * gcc-interface/trans.c (gigi): Switch to exception handler ABI #1. (Exception_Handler_to_gnu_gcc): Save the original cleanup returned by begin handler, pass it to end handler, and use EH_ELSE_EXPR to pass a propagating exception to end handler. (gnat_to_gnu): Leave the exception pointer alone for reraise. (add_cleanup): Handle EH_ELSE_EXPR, require it by itself. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@274029 138bc75d-0d04-0410-961f-82ee72b054a4
1 parent d9790ae commit 8754124

File tree

3 files changed

+316
-47
lines changed

3 files changed

+316
-47
lines changed

gcc/ada/ChangeLog

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
2019-08-02 Alexandre Oliva <[email protected]>
2+
3+
* libgnat/a-exexpr.adb (Begin_Handler_v1, End_Handler_v1): New.
4+
(Claimed_Cleanup): New.
5+
(Begin_Handler, End_Handler): Document.
6+
* gcc-interface/trans.c (gigi): Switch to exception handler
7+
ABI #1.
8+
(Exception_Handler_to_gnu_gcc): Save the original cleanup
9+
returned by begin handler, pass it to end handler, and use
10+
EH_ELSE_EXPR to pass a propagating exception to end handler.
11+
(gnat_to_gnu): Leave the exception pointer alone for reraise.
12+
(add_cleanup): Handle EH_ELSE_EXPR, require it by itself.
13+
114
2019-07-23 Ed Schonberg <[email protected]>
215

316
* sem_ch13.adb (Check_Aspect_At_End_Of_Declarations,

gcc/ada/gcc-interface/trans.c

Lines changed: 118 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -524,22 +524,27 @@ gigi (Node_Id gnat_root,
524524
NULL_TREE, is_default, true, true, true, false, false, NULL, Empty);
525525

526526
/* Hooks to call when entering/leaving an exception handler. */
527-
ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
528-
527+
ftype = build_function_type_list (ptr_type_node,
528+
ptr_type_node, NULL_TREE);
529529
begin_handler_decl
530-
= create_subprog_decl (get_identifier ("__gnat_begin_handler"), NULL_TREE,
531-
ftype, NULL_TREE,
530+
= create_subprog_decl (get_identifier ("__gnat_begin_handler_v1"),
531+
NULL_TREE, ftype, NULL_TREE,
532532
is_default, true, true, true, false, false, NULL,
533533
Empty);
534-
/* __gnat_begin_handler is a dummy procedure. */
534+
/* __gnat_begin_handler_v1 is not a dummy procedure, but we arrange
535+
for it not to throw. */
535536
TREE_NOTHROW (begin_handler_decl) = 1;
536537

538+
ftype = build_function_type_list (ptr_type_node,
539+
ptr_type_node, ptr_type_node,
540+
ptr_type_node, NULL_TREE);
537541
end_handler_decl
538-
= create_subprog_decl (get_identifier ("__gnat_end_handler"), NULL_TREE,
542+
= create_subprog_decl (get_identifier ("__gnat_end_handler_v1"), NULL_TREE,
539543
ftype, NULL_TREE,
540544
is_default, true, true, true, false, false, NULL,
541545
Empty);
542546

547+
ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
543548
unhandled_except_decl
544549
= create_subprog_decl (get_identifier ("__gnat_unhandled_except_handler"),
545550
NULL_TREE, ftype, NULL_TREE,
@@ -6201,59 +6206,120 @@ Exception_Handler_to_gnu_gcc (Node_Id gnat_node)
62016206
start_stmt_group ();
62026207
gnat_pushlevel ();
62036208

6204-
/* Expand a call to the begin_handler hook at the beginning of the handler,
6205-
and arrange for a call to the end_handler hook to occur on every possible
6206-
exit path.
6209+
/* Expand a call to the begin_handler hook at the beginning of the
6210+
handler, and arrange for a call to the end_handler hook to occur
6211+
on every possible exit path. GDB sets a breakpoint in the
6212+
begin_handler for catchpoints.
62076213
6208-
The hooks expect a pointer to the low level occurrence. This is required
6209-
for our stack management scheme because a raise inside the handler pushes
6210-
a new occurrence on top of the stack, which means that this top does not
6211-
necessarily match the occurrence this handler was dealing with.
6214+
A v1 begin handler saves the cleanup from the exception object,
6215+
and marks the exception as in use, so that it will not be
6216+
released by other handlers. A v1 end handler restores the
6217+
cleanup and releases the exception object, unless it is still
6218+
claimed, or the exception is being propagated (reraised).
62126219
62136220
__builtin_eh_pointer references the exception occurrence being
6214-
propagated. Upon handler entry, this is the exception for which the
6215-
handler is triggered. This might not be the case upon handler exit,
6216-
however, as we might have a new occurrence propagated by the handler's
6217-
body, and the end_handler hook called as a cleanup in this context.
6218-
6219-
We use a local variable to retrieve the incoming value at handler entry
6220-
time, and reuse it to feed the end_handler hook's argument at exit. */
6221-
6221+
handled or propagated. Within the handler region, it is the
6222+
former, but within the else branch of the EH_ELSE_EXPR, i.e. the
6223+
exceptional cleanup path, it is the latter, so we must save the
6224+
occurrence being handled early on, so that, should an exception
6225+
be (re)raised, we can release the current exception, or figure
6226+
out we're not to release it because we're propagating a reraise
6227+
thereof.
6228+
6229+
We use local variables to retrieve the incoming value at handler
6230+
entry time (EXPTR), the saved cleanup (EXCLN) and the token
6231+
(EXVTK), and reuse them to feed the end_handler hook's argument
6232+
at exit. */
6233+
6234+
/* CODE: void *EXPTR = __builtin_eh_pointer (0); */
62226235
tree gnu_current_exc_ptr
62236236
= build_call_expr (builtin_decl_explicit (BUILT_IN_EH_POINTER),
62246237
1, integer_zero_node);
6225-
tree prev_gnu_incoming_exc_ptr = gnu_incoming_exc_ptr;
6226-
gnu_incoming_exc_ptr
6238+
tree exc_ptr
62276239
= create_var_decl (get_identifier ("EXPTR"), NULL_TREE,
62286240
ptr_type_node, gnu_current_exc_ptr,
6229-
false, false, false, false, false, true, true,
6241+
true, false, false, false, false, true, true,
62306242
NULL, gnat_node);
62316243

6232-
add_stmt_with_node (build_call_n_expr (begin_handler_decl, 1,
6233-
gnu_incoming_exc_ptr),
6234-
gnat_node);
6244+
tree prev_gnu_incoming_exc_ptr = gnu_incoming_exc_ptr;
6245+
gnu_incoming_exc_ptr = exc_ptr;
6246+
6247+
/* begin_handler_decl must not throw, so we can use it as an
6248+
initializer for a variable used in cleanups.
6249+
6250+
CODE: void *EXCLN = __gnat_begin_handler_v1 (EXPTR); */
6251+
tree exc_cleanup
6252+
= create_var_decl (get_identifier ("EXCLN"), NULL_TREE,
6253+
ptr_type_node,
6254+
build_call_n_expr (begin_handler_decl, 1,
6255+
exc_ptr),
6256+
true, false, false, false, false,
6257+
true, true, NULL, gnat_node);
62356258

62366259
/* Declare and initialize the choice parameter, if present. */
62376260
if (Present (Choice_Parameter (gnat_node)))
62386261
{
62396262
tree gnu_param
62406263
= gnat_to_gnu_entity (Choice_Parameter (gnat_node), NULL_TREE, true);
62416264

6265+
/* CODE: __gnat_set_exception_parameter (&choice_param, EXPTR); */
62426266
add_stmt (build_call_n_expr
62436267
(set_exception_parameter_decl, 2,
62446268
build_unary_op (ADDR_EXPR, NULL_TREE, gnu_param),
62456269
gnu_incoming_exc_ptr));
62466270
}
62476271

6272+
/* CODE: <handler proper> */
62486273
add_stmt_list (Statements (gnat_node));
62496274

6250-
/* We don't have an End_Label at hand to set the location of the cleanup
6251-
actions, so we use that of the exception handler itself instead. */
6252-
tree stmt = build_call_n_expr (end_handler_decl, 1, gnu_incoming_exc_ptr);
6275+
tree call = build_call_n_expr (end_handler_decl, 3,
6276+
exc_ptr,
6277+
exc_cleanup,
6278+
null_pointer_node);
6279+
/* If the handler can only end by falling off the end, don't bother
6280+
with cleanups. */
62536281
if (stmt_list_cannot_alter_control_flow_p (Statements (gnat_node)))
6254-
add_stmt_with_node (stmt, gnat_node);
6282+
/* CODE: __gnat_end_handler_v1 (EXPTR, EXCLN, NULL); */
6283+
add_stmt_with_node (call, gnat_node);
6284+
/* Otherwise, all of the above is after
6285+
CODE: try {
6286+
6287+
The call above will appear after
6288+
CODE: } finally {
6289+
6290+
And the code below will appear after
6291+
CODE: } else {
6292+
6293+
The else block to a finally block is taken instead of the finally
6294+
block when an exception propagates out of the try block. */
62556295
else
6256-
add_cleanup (stmt, gnat_node);
6296+
{
6297+
start_stmt_group ();
6298+
gnat_pushlevel ();
6299+
/* CODE: void *EXPRP = __builtin_eh_handler (0); */
6300+
tree prop_ptr
6301+
= create_var_decl (get_identifier ("EXPRP"), NULL_TREE,
6302+
ptr_type_node,
6303+
build_call_expr (builtin_decl_explicit
6304+
(BUILT_IN_EH_POINTER),
6305+
1, integer_zero_node),
6306+
true, false, false, false, false,
6307+
true, true, NULL, gnat_node);
6308+
6309+
/* CODE: __gnat_end_handler_v1 (EXPTR, EXCLN, EXPRP); */
6310+
tree ecall = build_call_n_expr (end_handler_decl, 3,
6311+
exc_ptr,
6312+
exc_cleanup,
6313+
prop_ptr);
6314+
6315+
add_stmt_with_node (ecall, gnat_node);
6316+
6317+
/* CODE: } */
6318+
gnat_poplevel ();
6319+
tree eblk = end_stmt_group ();
6320+
tree ehls = build2 (EH_ELSE_EXPR, void_type_node, call, eblk);
6321+
add_cleanup (ehls, gnat_node);
6322+
}
62576323

62586324
gnat_poplevel ();
62596325

@@ -8270,19 +8336,11 @@ gnat_to_gnu (Node_Id gnat_node)
82708336
gcc_assert (No (Name (gnat_node)) && Back_End_Exceptions ());
82718337

82728338
start_stmt_group ();
8273-
gnat_pushlevel ();
82748339

8275-
/* Clear the current exception pointer so that the occurrence won't be
8276-
deallocated. */
8277-
gnu_expr = create_var_decl (get_identifier ("SAVED_EXPTR"), NULL_TREE,
8278-
ptr_type_node, gnu_incoming_exc_ptr,
8279-
false, false, false, false, false,
8280-
true, true, NULL, gnat_node);
8340+
add_stmt_with_node (build_call_n_expr (reraise_zcx_decl, 1,
8341+
gnu_incoming_exc_ptr),
8342+
gnat_node);
82818343

8282-
add_stmt (build_binary_op (MODIFY_EXPR, NULL_TREE, gnu_incoming_exc_ptr,
8283-
build_int_cst (ptr_type_node, 0)));
8284-
add_stmt (build_call_n_expr (reraise_zcx_decl, 1, gnu_expr));
8285-
gnat_poplevel ();
82868344
gnu_result = end_stmt_group ();
82878345
break;
82888346

@@ -9073,7 +9131,23 @@ add_cleanup (tree gnu_cleanup, Node_Id gnat_node)
90739131
{
90749132
if (Present (gnat_node))
90759133
set_expr_location_from_node (gnu_cleanup, gnat_node, true);
9076-
append_to_statement_list (gnu_cleanup, &current_stmt_group->cleanups);
9134+
/* An EH_ELSE_EXPR must be by itself, and that's all we need when we
9135+
use it. The assert below makes sure that is so. Should we ever
9136+
need more than that, we could combine EH_ELSE_EXPRs, and copy
9137+
non-EH_ELSE_EXPR stmts into both cleanup paths of an
9138+
EH_ELSE_EXPR. */
9139+
if (TREE_CODE (gnu_cleanup) == EH_ELSE_EXPR)
9140+
{
9141+
gcc_assert (!current_stmt_group->cleanups);
9142+
current_stmt_group->cleanups = gnu_cleanup;
9143+
}
9144+
else
9145+
{
9146+
gcc_assert (!current_stmt_group->cleanups
9147+
|| (TREE_CODE (current_stmt_group->cleanups)
9148+
!= EH_ELSE_EXPR));
9149+
append_to_statement_list (gnu_cleanup, &current_stmt_group->cleanups);
9150+
}
90779151
}
90789152

90799153
/* Set the BLOCK node corresponding to the current code group to GNU_BLOCK. */

0 commit comments

Comments
 (0)