summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/go/gofrontend/gogo-tree.cc18
-rw-r--r--gcc/go/gofrontend/gogo.cc18
-rw-r--r--gcc/go/gofrontend/runtime.def6
-rw-r--r--gcc/go/gofrontend/statements.cc9
-rw-r--r--libgo/runtime/go-defer.c10
-rw-r--r--libgo/runtime/go-defer.h7
-rw-r--r--libgo/runtime/go-panic.c6
-rw-r--r--libgo/runtime/go-unwind.c10
8 files changed, 57 insertions, 27 deletions
diff --git a/gcc/go/gofrontend/gogo-tree.cc b/gcc/go/gofrontend/gogo-tree.cc
index fbddc0492f6..49a0ba40bbd 100644
--- a/gcc/go/gofrontend/gogo-tree.cc
+++ b/gcc/go/gofrontend/gogo-tree.cc
@@ -1592,15 +1592,25 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function,
&& !this->type_->results()->empty()
&& !this->type_->results()->front().name().empty())
{
- // If the result variables are named, we need to return them
- // again, because they might have been changed by a defer
- // function.
+ // If the result variables are named, and we are returning from
+ // this function rather than panicing through it, we need to
+ // return them again, because they might have been changed by a
+ // defer function. The runtime routines set the defer_stack
+ // variable to true if we are returning from this function.
retval = this->return_value(gogo, named_function, end_loc,
&stmt_list);
set = fold_build2_loc(end_loc, MODIFY_EXPR, void_type_node,
DECL_RESULT(this->fndecl_), retval);
ret_stmt = fold_build1_loc(end_loc, RETURN_EXPR, void_type_node, set);
- append_to_statement_list(ret_stmt, &stmt_list);
+
+ Expression* ref =
+ Expression::make_temporary_reference(this->defer_stack_, end_loc);
+ tree tref = ref->get_tree(&context);
+ tree s = build3_loc(end_loc, COND_EXPR, void_type_node, tref,
+ ret_stmt, NULL_TREE);
+
+ append_to_statement_list(s, &stmt_list);
+
}
go_assert(*fini == NULL_TREE);
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index c544eba8d2f..4a89ca80ba5 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -2976,27 +2976,27 @@ Function::determine_types()
this->block_->determine_types();
}
-// Get a pointer to the variable holding the defer stack for this
-// function, making it if necessary. At least at present, the value
-// of this variable is not used. However, a pointer to this variable
-// is used as a marker for the functions on the defer stack associated
-// with this function. Doing things this way permits inlining a
+// Get a pointer to the variable representing the defer stack for this
+// function, making it if necessary. The value of the variable is set
+// by the runtime routines to true if the function is returning,
+// rather than panicing through. A pointer to this variable is used
+// as a marker for the functions on the defer stack associated with
+// this function. A function-specific variable permits inlining a
// function which uses defer.
Expression*
Function::defer_stack(source_location location)
{
- Type* t = Type::make_pointer_type(Type::make_void_type());
if (this->defer_stack_ == NULL)
{
- Expression* n = Expression::make_nil(location);
+ Type* t = Type::lookup_bool_type();
+ Expression* n = Expression::make_boolean(false, location);
this->defer_stack_ = Statement::make_temporary(t, n, location);
this->defer_stack_->set_is_address_taken();
}
Expression* ref = Expression::make_temporary_reference(this->defer_stack_,
location);
- Expression* addr = Expression::make_unary(OPERATOR_AND, ref, location);
- return Expression::make_unsafe_cast(t, addr, location);
+ return Expression::make_unary(OPERATOR_AND, ref, location);
}
// Export the function.
diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def
index 219ccb8bd90..a7828edeb2e 100644
--- a/gcc/go/gofrontend/runtime.def
+++ b/gcc/go/gofrontend/runtime.def
@@ -165,10 +165,10 @@ DEF_GO_RUNTIME(SET_DEFER_RETADDR, "__go_set_defer_retaddr", P1(POINTER),
R1(BOOL))
// Check for a deferred function in an exception handler.
-DEF_GO_RUNTIME(CHECK_DEFER, "__go_check_defer", P1(POINTER), R0())
+DEF_GO_RUNTIME(CHECK_DEFER, "__go_check_defer", P1(BOOLPTR), R0())
// Run deferred functions.
-DEF_GO_RUNTIME(UNDEFER, "__go_undefer", P1(POINTER), R0())
+DEF_GO_RUNTIME(UNDEFER, "__go_undefer", P1(BOOLPTR), R0())
// Panic with a runtime error.
DEF_GO_RUNTIME(RUNTIME_ERROR, "__go_runtime_error", P1(INT), R0())
@@ -207,7 +207,7 @@ DEF_GO_RUNTIME(GO, "__go_go", P2(FUNC_PTR, POINTER), R0())
// Defer a function.
-DEF_GO_RUNTIME(DEFER, "__go_defer", P3(POINTER, FUNC_PTR, POINTER), R0())
+DEF_GO_RUNTIME(DEFER, "__go_defer", P3(BOOLPTR, FUNC_PTR, POINTER), R0())
// Run a select statement.
diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index bb43dcdee43..82be1129028 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -2539,11 +2539,10 @@ Return_statement::do_traverse_assignments(Traverse_assignments* tassign)
// Lower a return statement. If we are returning a function call
// which returns multiple values which match the current function,
-// split up the call's results. If the function has named result
-// variables, and the return statement lists explicit values, then
-// implement it by assigning the values to the result variables and
-// changing the statement to not list any values. This lets
-// panic/recover work correctly.
+// split up the call's results. If the return statement lists
+// explicit values, implement this statement by assigning the values
+// to the result variables and change this statement to a naked
+// return. This lets panic/recover work correctly.
Statement*
Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing,
diff --git a/libgo/runtime/go-defer.c b/libgo/runtime/go-defer.c
index 6425f0586a7..1f116eb38c9 100644
--- a/libgo/runtime/go-defer.c
+++ b/libgo/runtime/go-defer.c
@@ -13,7 +13,7 @@
/* This function is called each time we need to defer a call. */
void
-__go_defer (void *frame, void (*pfn) (void *), void *arg)
+__go_defer (_Bool *frame, void (*pfn) (void *), void *arg)
{
struct __go_defer_stack *n;
@@ -34,7 +34,7 @@ __go_defer (void *frame, void (*pfn) (void *), void *arg)
/* This function is called when we want to undefer the stack. */
void
-__go_undefer (void *frame)
+__go_undefer (_Bool *frame)
{
if (__go_panic_defer == NULL)
return;
@@ -53,6 +53,12 @@ __go_undefer (void *frame)
__go_panic_defer->__defer = d->__next;
__go_free (d);
+
+ /* Since we are executing a defer function here, we know we are
+ returning from the calling function. If the calling
+ function, or one of its callees, paniced, then the defer
+ functions would be executed by __go_panic. */
+ *frame = 1;
}
}
diff --git a/libgo/runtime/go-defer.h b/libgo/runtime/go-defer.h
index f8924f3b63a..0b20e8f6e78 100644
--- a/libgo/runtime/go-defer.h
+++ b/libgo/runtime/go-defer.h
@@ -13,9 +13,10 @@ struct __go_defer_stack
/* The next entry in the stack. */
struct __go_defer_stack *__next;
- /* The frame pointer for the function which called this defer
- statement. */
- void *__frame;
+ /* The stack variable for the function which called this defer
+ statement. This is set to 1 if we are returning from that
+ function, 0 if we are panicing through it. */
+ _Bool *__frame;
/* The value of the panic stack when this function is deferred.
This function can not recover this value from the panic stack.
diff --git a/libgo/runtime/go-panic.c b/libgo/runtime/go-panic.c
index b684779cda8..c39ea9f9303 100644
--- a/libgo/runtime/go-panic.c
+++ b/libgo/runtime/go-panic.c
@@ -87,6 +87,12 @@ __go_panic (struct __go_empty_interface arg)
/* __go_unwind_stack should not return. */
abort ();
}
+
+ /* Because we executed that defer function by a panic, and
+ it did not call recover, we know that we are not
+ returning from the calling function--we are panicing
+ through it. */
+ *d->__frame = 0;
}
__go_panic_defer->__defer = d->__next;
diff --git a/libgo/runtime/go-unwind.c b/libgo/runtime/go-unwind.c
index 0bc3f1b4e65..e64cf903d9f 100644
--- a/libgo/runtime/go-unwind.c
+++ b/libgo/runtime/go-unwind.c
@@ -44,7 +44,7 @@ static const _Unwind_Exception_Class __go_exception_class =
continue unwinding. */
void
-__go_check_defer (void *frame)
+__go_check_defer (_Bool *frame)
{
struct _Unwind_Exception *hdr;
@@ -103,8 +103,12 @@ __go_check_defer (void *frame)
if (was_recovered)
{
/* Just return and continue executing Go code. */
+ *frame = 1;
return;
}
+
+ /* We are panicing through this function. */
+ *frame = 0;
}
else if (__go_panic_defer->__defer != NULL
&& __go_panic_defer->__defer->__pfn == NULL
@@ -118,6 +122,10 @@ __go_check_defer (void *frame)
d = __go_panic_defer->__defer;
__go_panic_defer->__defer = d->__next;
__go_free (d);
+
+ /* We are returning from this function. */
+ *frame = 1;
+
return;
}