Change defer stack from tree to Expression.
From-SVN: r172402
+2011-04-13 Ian Lance Taylor <iant@google.com>
+
+ * Make-lang.in (go/gogo-tree.o): depend on $(GO_RUNTIME_H).
+
2011-04-13 Ian Lance Taylor <iant@google.com>
* Make-lang.in (GO_OBJS): Add go/runtime.o.
go/gogo-tree.o: go/gofrontend/gogo-tree.cc $(GO_SYSTEM_H) $(TOPLEV_H) \
$(TREE_H) $(GIMPLE_H) tree-iterator.h $(CGRAPH_H) langhooks.h \
convert.h output.h $(DIAGNOSTIC_H) $(GO_TYPES_H) \
- $(GO_EXPRESSIONS_H) $(GO_STATEMENTS_H) $(GO_GOGO_H)
+ $(GO_EXPRESSIONS_H) $(GO_STATEMENTS_H) $(GO_RUNTIME_H) $(GO_GOGO_H)
go/gogo.o: go/gofrontend/gogo.cc $(GO_SYSTEM_H) $(GO_C_H) \
go/gofrontend/go-dump.h $(GO_LEX_H) $(GO_TYPES_H) $(GO_STATEMENTS_H) \
$(GO_EXPRESSIONS_H) go/gofrontend/dataflow.h $(GO_RUNTIME_H) \
#include "types.h"
#include "expressions.h"
#include "statements.h"
+#include "runtime.h"
#include "gogo.h"
// Whether we have seen any errors.
// Declare variables if necessary.
tree bind = NULL_TREE;
- if (declare_vars != NULL_TREE)
+ tree defer_init = NULL_TREE;
+ if (declare_vars != NULL_TREE || this->defer_stack_ != NULL)
{
tree block = make_node(BLOCK);
BLOCK_SUPERCONTEXT(block) = fndecl;
DECL_INITIAL(fndecl) = block;
BLOCK_VARS(block) = declare_vars;
TREE_USED(block) = 1;
+
+ if (this->defer_stack_ != NULL)
+ {
+ Translate_context dcontext(gogo, named_function, this->block_,
+ block);
+ defer_init = this->defer_stack_->get_tree(&dcontext);
+ }
+
bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block),
NULL_TREE, block);
TREE_SIDE_EFFECTS(bind) = 1;
// If we have a defer stack, initialize it at the start of a
// function.
- if (this->defer_stack_ != NULL_TREE)
+ if (defer_init != NULL_TREE && defer_init != error_mark_node)
{
- tree defer_init = build1(DECL_EXPR, void_type_node,
- this->defer_stack_);
SET_EXPR_LOCATION(defer_init, this->block_->start_location());
append_to_statement_list(defer_init, &init);
// purpose is to stop the stack unwinding if a deferred function
// calls recover. There are more details in
// libgo/runtime/go-unwind.c.
+
tree stmt_list = NULL_TREE;
- static tree check_fndecl;
- tree call = Gogo::call_builtin(&check_fndecl,
- end_loc,
- "__go_check_defer",
- 1,
- void_type_node,
- ptr_type_node,
- this->defer_stack(end_loc));
- if (call != error_mark_node)
- append_to_statement_list(call, &stmt_list);
+
+ Expression* call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1,
+ this->defer_stack(end_loc));
+ Translate_context context(gogo, named_function, NULL, NULL);
+ tree call_tree = call->get_tree(&context);
+ if (call_tree != error_mark_node)
+ append_to_statement_list(call_tree, &stmt_list);
tree retval = this->return_value(gogo, named_function, end_loc, &stmt_list);
tree set;
label);
append_to_statement_list(define_label, &stmt_list);
- static tree undefer_fndecl;
- tree undefer = Gogo::call_builtin(&undefer_fndecl,
- end_loc,
- "__go_undefer",
- 1,
- void_type_node,
- ptr_type_node,
- this->defer_stack(end_loc));
- if (undefer_fndecl != NULL_TREE)
- TREE_NOTHROW(undefer_fndecl) = 0;
-
- tree defer = Gogo::call_builtin(&check_fndecl,
- end_loc,
- "__go_check_defer",
- 1,
- void_type_node,
- ptr_type_node,
- this->defer_stack(end_loc));
+ call = Runtime::make_call(Runtime::UNDEFER, end_loc, 1,
+ this->defer_stack(end_loc));
+ tree undefer = call->get_tree(&context);
+
+ call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1,
+ this->defer_stack(end_loc));
+ tree defer = call->get_tree(&context);
+
+ if (undefer == error_mark_node || defer == error_mark_node)
+ return;
+
tree jump = fold_build1_loc(end_loc, GOTO_EXPR, void_type_node, label);
tree catch_body = build2(COMPOUND_EXPR, void_type_node, defer, jump);
catch_body = build2(CATCH_EXPR, void_type_node, NULL, catch_body);
}
}
-// Get the tree for the variable holding the defer stack for this
-// function. 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 function which uses defer.
-
-tree
-Function::defer_stack(source_location location)
-{
- if (this->defer_stack_ == NULL_TREE)
- {
- tree var = create_tmp_var(ptr_type_node, "DEFER");
- DECL_INITIAL(var) = null_pointer_node;
- DECL_SOURCE_LOCATION(var) = location;
- TREE_ADDRESSABLE(var) = 1;
- this->defer_stack_ = var;
- }
- return fold_convert_loc(location, ptr_type_node,
- build_fold_addr_expr_loc(location,
- this->defer_stack_));
-}
-
// Get a tree for the statements in a block.
tree
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
+// 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);
+ 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);
+}
+
// Export the function.
void
class Function_type;
class Expression;
class Statement;
+class Temporary_statement;
class Block;
class Function;
class Bindings;
return_value(Gogo*, Named_object*, source_location, tree* stmt_list) const;
// Get a tree for the variable holding the defer stack.
- tree
+ Expression*
defer_stack(source_location);
// Export the function.
Labels labels_;
// The function decl.
tree fndecl_;
- // A variable holding the defer stack variable. This is NULL unless
- // we actually need a defer stack.
- tree defer_stack_;
+ // The defer stack variable. A pointer to this variable is used to
+ // distinguish the defer stack for one function from another. This
+ // is NULL unless we actually need a defer stack.
+ Temporary_statement* defer_stack_;
// True if the result variables are named.
bool results_are_named_;
// True if this function calls the predeclared recover function.
{
public:
Simplify_thunk_traverse(Gogo* gogo)
- : Traverse(traverse_blocks),
- gogo_(gogo)
+ : Traverse(traverse_functions | traverse_blocks),
+ gogo_(gogo), function_(NULL)
{ }
+ int
+ function(Named_object*);
+
int
block(Block*);
private:
+ // General IR.
Gogo* gogo_;
+ // The function we are traversing.
+ Named_object* function_;
};
+// Keep track of the current function while looking for thunks.
+
+int
+Simplify_thunk_traverse::function(Named_object* no)
+{
+ gcc_assert(this->function_ == NULL);
+ this->function_ = no;
+ int t = no->func_value()->traverse(this);
+ this->function_ = NULL;
+ if (t == TRAVERSE_EXIT)
+ return t;
+ return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// Look for thunks in a block.
+
int
Simplify_thunk_traverse::block(Block* b)
{
Thunk_statement* stat = b->statements()->back()->thunk_statement();
if (stat == NULL)
return TRAVERSE_CONTINUE;
- if (stat->simplify_statement(this->gogo_, b))
+ if (stat->simplify_statement(this->gogo_, this->function_, b))
return TRAVERSE_SKIP_COMPONENTS;
return TRAVERSE_CONTINUE;
}
// struct to a thunk. The thunk does the real call.
bool
-Thunk_statement::simplify_statement(Gogo* gogo, Block* block)
+Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
+ Block* block)
{
if (this->classification() == STATEMENT_ERROR)
return false;
if (this->call_->is_error_expression())
return false;
+ if (this->classification() == STATEMENT_DEFER)
+ {
+ // Make sure that the defer stack exists for the function. We
+ // will use when converting this statement to the backend
+ // representation, but we want it to exist when we start
+ // converting the function.
+ function->func_value()->defer_stack(this->location());
+ }
+
Call_expression* ce = this->call_->call_expression();
Function_type* fntype = ce->get_function_type();
if (fntype == NULL)
// Get the function and argument trees.
-void
-Thunk_statement::get_fn_and_arg(Translate_context* context, tree* pfn,
- tree* parg)
+bool
+Thunk_statement::get_fn_and_arg(Expression** pfn, Expression** parg)
{
if (this->call_->is_error_expression())
- {
- *pfn = error_mark_node;
- *parg = error_mark_node;
- return;
- }
+ return false;
Call_expression* ce = this->call_->call_expression();
- Expression* fn = ce->fn();
- *pfn = fn->get_tree(context);
+ *pfn = ce->fn();
const Expression_list* args = ce->args();
if (args == NULL || args->empty())
- *parg = null_pointer_node;
+ *parg = Expression::make_nil(this->location());
else
{
gcc_assert(args->size() == 1);
- *parg = args->front()->get_tree(context);
+ *parg = args->front();
}
+
+ return true;
}
// Class Go_statement.
tree
Go_statement::do_get_tree(Translate_context* context)
{
- tree fn_tree;
- tree arg_tree;
- this->get_fn_and_arg(context, &fn_tree, &arg_tree);
-
- static tree go_fndecl;
-
- tree fn_arg_type = NULL_TREE;
- if (go_fndecl == NULL_TREE)
- {
- // Only build FN_ARG_TYPE if we need it.
- tree subargtypes = tree_cons(NULL_TREE, ptr_type_node, void_list_node);
- tree subfntype = build_function_type(ptr_type_node, subargtypes);
- fn_arg_type = build_pointer_type(subfntype);
- }
+ Expression* fn;
+ Expression* arg;
+ if (!this->get_fn_and_arg(&fn, &arg))
+ return error_mark_node;
- return Gogo::call_builtin(&go_fndecl,
- this->location(),
- "__go_go",
- 2,
- void_type_node,
- fn_arg_type,
- fn_tree,
- ptr_type_node,
- arg_tree);
+ Expression* call = Runtime::make_call(Runtime::GO, this->location(), 2,
+ fn, arg);
+ tree call_tree = call->get_tree(context);
+ Bexpression* call_bexpr = tree_to_expr(call_tree);
+ Bstatement* ret = context->backend()->expression_statement(call_bexpr);
+ return stat_to_tree(ret);
}
// Make a go statement.
tree
Defer_statement::do_get_tree(Translate_context* context)
{
- source_location loc = this->location();
-
- tree fn_tree;
- tree arg_tree;
- this->get_fn_and_arg(context, &fn_tree, &arg_tree);
- if (fn_tree == error_mark_node || arg_tree == error_mark_node)
+ Expression* fn;
+ Expression* arg;
+ if (!this->get_fn_and_arg(&fn, &arg))
return error_mark_node;
- static tree defer_fndecl;
-
- tree fn_arg_type = NULL_TREE;
- if (defer_fndecl == NULL_TREE)
- {
- // Only build FN_ARG_TYPE if we need it.
- tree subargtypes = tree_cons(NULL_TREE, ptr_type_node, void_list_node);
- tree subfntype = build_function_type(ptr_type_node, subargtypes);
- fn_arg_type = build_pointer_type(subfntype);
- }
+ source_location loc = this->location();
+ Expression* ds = context->function()->func_value()->defer_stack(loc);
- tree defer_stack = context->function()->func_value()->defer_stack(loc);
-
- return Gogo::call_builtin(&defer_fndecl,
- loc,
- "__go_defer",
- 3,
- void_type_node,
- ptr_type_node,
- defer_stack,
- fn_arg_type,
- fn_tree,
- ptr_type_node,
- arg_tree);
+ Expression* call = Runtime::make_call(Runtime::DEFER, loc, 3,
+ ds, fn, arg);
+ tree call_tree = call->get_tree(context);
+ Bexpression* call_bexpr = tree_to_expr(call_tree);
+ Bstatement* ret = context->backend()->expression_statement(call_bexpr);
+ return stat_to_tree(ret);
}
// Make a defer statement.
// Simplify a go or defer statement so that it only uses a single
// parameter.
bool
- simplify_statement(Gogo*, Block*);
+ simplify_statement(Gogo*, Named_object*, Block*);
protected:
int
do_check_types(Gogo*);
// Return the function and argument trees for the call.
- void
- get_fn_and_arg(Translate_context*, tree* pfn, tree* parg);
+ bool
+ get_fn_and_arg(Expression** pfn, Expression** parg);
private:
// Return whether this is a simple go statement.