Commit 8dc421e0 by Ian Lance Taylor Committed by Ian Lance Taylor

Use backend interface for go and defer statements.

Change defer stack from tree to Expression.

From-SVN: r172402
parent 123c516a
2011-04-13 Ian Lance Taylor <iant@google.com> 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. * Make-lang.in (GO_OBJS): Add go/runtime.o.
(GO_RUNTIME_H): New variable. (GO_RUNTIME_H): New variable.
(go/runtime.o): New target. (go/runtime.o): New target.
......
...@@ -262,7 +262,7 @@ go/go-dump.o: go/gofrontend/go-dump.cc $(GO_SYSTEM_H) $(GO_C_H) \ ...@@ -262,7 +262,7 @@ go/go-dump.o: go/gofrontend/go-dump.cc $(GO_SYSTEM_H) $(GO_C_H) \
go/gogo-tree.o: go/gofrontend/gogo-tree.cc $(GO_SYSTEM_H) $(TOPLEV_H) \ 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 \ $(TREE_H) $(GIMPLE_H) tree-iterator.h $(CGRAPH_H) langhooks.h \
convert.h output.h $(DIAGNOSTIC_H) $(GO_TYPES_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/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/gofrontend/go-dump.h $(GO_LEX_H) $(GO_TYPES_H) $(GO_STATEMENTS_H) \
$(GO_EXPRESSIONS_H) go/gofrontend/dataflow.h $(GO_RUNTIME_H) \ $(GO_EXPRESSIONS_H) go/gofrontend/dataflow.h $(GO_RUNTIME_H) \
......
...@@ -31,6 +31,7 @@ extern "C" ...@@ -31,6 +31,7 @@ extern "C"
#include "types.h" #include "types.h"
#include "expressions.h" #include "expressions.h"
#include "statements.h" #include "statements.h"
#include "runtime.h"
#include "gogo.h" #include "gogo.h"
// Whether we have seen any errors. // Whether we have seen any errors.
...@@ -1585,13 +1586,22 @@ Function::build_tree(Gogo* gogo, Named_object* named_function) ...@@ -1585,13 +1586,22 @@ Function::build_tree(Gogo* gogo, Named_object* named_function)
// Declare variables if necessary. // Declare variables if necessary.
tree bind = NULL_TREE; 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); tree block = make_node(BLOCK);
BLOCK_SUPERCONTEXT(block) = fndecl; BLOCK_SUPERCONTEXT(block) = fndecl;
DECL_INITIAL(fndecl) = block; DECL_INITIAL(fndecl) = block;
BLOCK_VARS(block) = declare_vars; BLOCK_VARS(block) = declare_vars;
TREE_USED(block) = 1; 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), bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block),
NULL_TREE, block); NULL_TREE, block);
TREE_SIDE_EFFECTS(bind) = 1; TREE_SIDE_EFFECTS(bind) = 1;
...@@ -1615,10 +1625,8 @@ Function::build_tree(Gogo* gogo, Named_object* named_function) ...@@ -1615,10 +1625,8 @@ Function::build_tree(Gogo* gogo, Named_object* named_function)
// If we have a defer stack, initialize it at the start of a // If we have a defer stack, initialize it at the start of a
// function. // 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()); SET_EXPR_LOCATION(defer_init, this->block_->start_location());
append_to_statement_list(defer_init, &init); append_to_statement_list(defer_init, &init);
...@@ -1663,17 +1671,15 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function, ...@@ -1663,17 +1671,15 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function,
// purpose is to stop the stack unwinding if a deferred function // purpose is to stop the stack unwinding if a deferred function
// calls recover. There are more details in // calls recover. There are more details in
// libgo/runtime/go-unwind.c. // libgo/runtime/go-unwind.c.
tree stmt_list = NULL_TREE; tree stmt_list = NULL_TREE;
static tree check_fndecl;
tree call = Gogo::call_builtin(&check_fndecl, Expression* call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1,
end_loc, this->defer_stack(end_loc));
"__go_check_defer", Translate_context context(gogo, named_function, NULL, NULL);
1, tree call_tree = call->get_tree(&context);
void_type_node, if (call_tree != error_mark_node)
ptr_type_node, append_to_statement_list(call_tree, &stmt_list);
this->defer_stack(end_loc));
if (call != error_mark_node)
append_to_statement_list(call, &stmt_list);
tree retval = this->return_value(gogo, named_function, end_loc, &stmt_list); tree retval = this->return_value(gogo, named_function, end_loc, &stmt_list);
tree set; tree set;
...@@ -1704,24 +1710,17 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function, ...@@ -1704,24 +1710,17 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function,
label); label);
append_to_statement_list(define_label, &stmt_list); append_to_statement_list(define_label, &stmt_list);
static tree undefer_fndecl; call = Runtime::make_call(Runtime::UNDEFER, end_loc, 1,
tree undefer = Gogo::call_builtin(&undefer_fndecl, this->defer_stack(end_loc));
end_loc, tree undefer = call->get_tree(&context);
"__go_undefer",
1, call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1,
void_type_node, this->defer_stack(end_loc));
ptr_type_node, tree defer = call->get_tree(&context);
this->defer_stack(end_loc));
if (undefer_fndecl != NULL_TREE) if (undefer == error_mark_node || defer == error_mark_node)
TREE_NOTHROW(undefer_fndecl) = 0; return;
tree defer = Gogo::call_builtin(&check_fndecl,
end_loc,
"__go_check_defer",
1,
void_type_node,
ptr_type_node,
this->defer_stack(end_loc));
tree jump = fold_build1_loc(end_loc, GOTO_EXPR, void_type_node, label); tree jump = fold_build1_loc(end_loc, GOTO_EXPR, void_type_node, label);
tree catch_body = build2(COMPOUND_EXPR, void_type_node, defer, jump); tree catch_body = build2(COMPOUND_EXPR, void_type_node, defer, jump);
catch_body = build2(CATCH_EXPR, void_type_node, NULL, catch_body); catch_body = build2(CATCH_EXPR, void_type_node, NULL, catch_body);
...@@ -1794,28 +1793,6 @@ Function::return_value(Gogo* gogo, Named_object* named_function, ...@@ -1794,28 +1793,6 @@ Function::return_value(Gogo* gogo, Named_object* named_function,
} }
} }
// 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. // Get a tree for the statements in a block.
tree tree
......
...@@ -2884,6 +2884,29 @@ Function::determine_types() ...@@ -2884,6 +2884,29 @@ Function::determine_types()
this->block_->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
// 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. // Export the function.
void void
......
...@@ -17,6 +17,7 @@ class Typed_identifier_list; ...@@ -17,6 +17,7 @@ class Typed_identifier_list;
class Function_type; class Function_type;
class Expression; class Expression;
class Statement; class Statement;
class Temporary_statement;
class Block; class Block;
class Function; class Function;
class Bindings; class Bindings;
...@@ -977,7 +978,7 @@ class Function ...@@ -977,7 +978,7 @@ class Function
return_value(Gogo*, Named_object*, source_location, tree* stmt_list) const; return_value(Gogo*, Named_object*, source_location, tree* stmt_list) const;
// Get a tree for the variable holding the defer stack. // Get a tree for the variable holding the defer stack.
tree Expression*
defer_stack(source_location); defer_stack(source_location);
// Export the function. // Export the function.
...@@ -1033,9 +1034,10 @@ class Function ...@@ -1033,9 +1034,10 @@ class Function
Labels labels_; Labels labels_;
// The function decl. // The function decl.
tree fndecl_; tree fndecl_;
// A variable holding the defer stack variable. This is NULL unless // The defer stack variable. A pointer to this variable is used to
// we actually need a defer stack. // distinguish the defer stack for one function from another. This
tree defer_stack_; // is NULL unless we actually need a defer stack.
Temporary_statement* defer_stack_;
// True if the result variables are named. // True if the result variables are named.
bool results_are_named_; bool results_are_named_;
// True if this function calls the predeclared recover function. // True if this function calls the predeclared recover function.
......
...@@ -1718,17 +1718,39 @@ class Simplify_thunk_traverse : public Traverse ...@@ -1718,17 +1718,39 @@ class Simplify_thunk_traverse : public Traverse
{ {
public: public:
Simplify_thunk_traverse(Gogo* gogo) Simplify_thunk_traverse(Gogo* gogo)
: Traverse(traverse_blocks), : Traverse(traverse_functions | traverse_blocks),
gogo_(gogo) gogo_(gogo), function_(NULL)
{ } { }
int int
function(Named_object*);
int
block(Block*); block(Block*);
private: private:
// General IR.
Gogo* gogo_; 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 int
Simplify_thunk_traverse::block(Block* b) Simplify_thunk_traverse::block(Block* b)
{ {
...@@ -1739,7 +1761,7 @@ Simplify_thunk_traverse::block(Block* b) ...@@ -1739,7 +1761,7 @@ Simplify_thunk_traverse::block(Block* b)
Thunk_statement* stat = b->statements()->back()->thunk_statement(); Thunk_statement* stat = b->statements()->back()->thunk_statement();
if (stat == NULL) if (stat == NULL)
return TRAVERSE_CONTINUE; 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_SKIP_COMPONENTS;
return TRAVERSE_CONTINUE; return TRAVERSE_CONTINUE;
} }
...@@ -1761,13 +1783,23 @@ Gogo::simplify_thunk_statements() ...@@ -1761,13 +1783,23 @@ Gogo::simplify_thunk_statements()
// struct to a thunk. The thunk does the real call. // struct to a thunk. The thunk does the real call.
bool 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) if (this->classification() == STATEMENT_ERROR)
return false; return false;
if (this->call_->is_error_expression()) if (this->call_->is_error_expression())
return false; 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(); Call_expression* ce = this->call_->call_expression();
Function_type* fntype = ce->get_function_type(); Function_type* fntype = ce->get_function_type();
if (fntype == NULL) if (fntype == NULL)
...@@ -2160,30 +2192,26 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name, ...@@ -2160,30 +2192,26 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
// Get the function and argument trees. // Get the function and argument trees.
void bool
Thunk_statement::get_fn_and_arg(Translate_context* context, tree* pfn, Thunk_statement::get_fn_and_arg(Expression** pfn, Expression** parg)
tree* parg)
{ {
if (this->call_->is_error_expression()) if (this->call_->is_error_expression())
{ return false;
*pfn = error_mark_node;
*parg = error_mark_node;
return;
}
Call_expression* ce = this->call_->call_expression(); Call_expression* ce = this->call_->call_expression();
Expression* fn = ce->fn(); *pfn = ce->fn();
*pfn = fn->get_tree(context);
const Expression_list* args = ce->args(); const Expression_list* args = ce->args();
if (args == NULL || args->empty()) if (args == NULL || args->empty())
*parg = null_pointer_node; *parg = Expression::make_nil(this->location());
else else
{ {
gcc_assert(args->size() == 1); gcc_assert(args->size() == 1);
*parg = args->front()->get_tree(context); *parg = args->front();
} }
return true;
} }
// Class Go_statement. // Class Go_statement.
...@@ -2191,30 +2219,17 @@ Thunk_statement::get_fn_and_arg(Translate_context* context, tree* pfn, ...@@ -2191,30 +2219,17 @@ Thunk_statement::get_fn_and_arg(Translate_context* context, tree* pfn,
tree tree
Go_statement::do_get_tree(Translate_context* context) Go_statement::do_get_tree(Translate_context* context)
{ {
tree fn_tree; Expression* fn;
tree arg_tree; Expression* arg;
this->get_fn_and_arg(context, &fn_tree, &arg_tree); if (!this->get_fn_and_arg(&fn, &arg))
return error_mark_node;
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);
}
return Gogo::call_builtin(&go_fndecl, Expression* call = Runtime::make_call(Runtime::GO, this->location(), 2,
this->location(), fn, arg);
"__go_go", tree call_tree = call->get_tree(context);
2, Bexpression* call_bexpr = tree_to_expr(call_tree);
void_type_node, Bstatement* ret = context->backend()->expression_statement(call_bexpr);
fn_arg_type, return stat_to_tree(ret);
fn_tree,
ptr_type_node,
arg_tree);
} }
// Make a go statement. // Make a go statement.
...@@ -2230,38 +2245,20 @@ Statement::make_go_statement(Call_expression* call, source_location location) ...@@ -2230,38 +2245,20 @@ Statement::make_go_statement(Call_expression* call, source_location location)
tree tree
Defer_statement::do_get_tree(Translate_context* context) Defer_statement::do_get_tree(Translate_context* context)
{ {
source_location loc = this->location(); Expression* fn;
Expression* arg;
tree fn_tree; if (!this->get_fn_and_arg(&fn, &arg))
tree arg_tree;
this->get_fn_and_arg(context, &fn_tree, &arg_tree);
if (fn_tree == error_mark_node || arg_tree == error_mark_node)
return error_mark_node; return error_mark_node;
static tree defer_fndecl; source_location loc = this->location();
Expression* ds = context->function()->func_value()->defer_stack(loc);
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);
}
tree defer_stack = context->function()->func_value()->defer_stack(loc); Expression* call = Runtime::make_call(Runtime::DEFER, loc, 3,
ds, fn, arg);
return Gogo::call_builtin(&defer_fndecl, tree call_tree = call->get_tree(context);
loc, Bexpression* call_bexpr = tree_to_expr(call_tree);
"__go_defer", Bstatement* ret = context->backend()->expression_statement(call_bexpr);
3, return stat_to_tree(ret);
void_type_node,
ptr_type_node,
defer_stack,
fn_arg_type,
fn_tree,
ptr_type_node,
arg_tree);
} }
// Make a defer statement. // Make a defer statement.
......
...@@ -852,7 +852,7 @@ class Thunk_statement : public Statement ...@@ -852,7 +852,7 @@ class Thunk_statement : public Statement
// Simplify a go or defer statement so that it only uses a single // Simplify a go or defer statement so that it only uses a single
// parameter. // parameter.
bool bool
simplify_statement(Gogo*, Block*); simplify_statement(Gogo*, Named_object*, Block*);
protected: protected:
int int
...@@ -868,8 +868,8 @@ class Thunk_statement : public Statement ...@@ -868,8 +868,8 @@ class Thunk_statement : public Statement
do_check_types(Gogo*); do_check_types(Gogo*);
// Return the function and argument trees for the call. // Return the function and argument trees for the call.
void bool
get_fn_and_arg(Translate_context*, tree* pfn, tree* parg); get_fn_and_arg(Expression** pfn, Expression** parg);
private: private:
// Return whether this is a simple go statement. // Return whether this is a simple go statement.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment