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>
* 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_RUNTIME_H): New variable.
(go/runtime.o): New target.
......
......@@ -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) \
$(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) \
......
......@@ -31,6 +31,7 @@ extern "C"
#include "types.h"
#include "expressions.h"
#include "statements.h"
#include "runtime.h"
#include "gogo.h"
// Whether we have seen any errors.
......@@ -1585,13 +1586,22 @@ Function::build_tree(Gogo* gogo, Named_object* named_function)
// 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;
......@@ -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
// 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);
......@@ -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
// 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;
......@@ -1704,24 +1710,17 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function,
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);
......@@ -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.
tree
......
......@@ -2884,6 +2884,29 @@ 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
// 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
......
......@@ -17,6 +17,7 @@ class Typed_identifier_list;
class Function_type;
class Expression;
class Statement;
class Temporary_statement;
class Block;
class Function;
class Bindings;
......@@ -977,7 +978,7 @@ class Function
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.
......@@ -1033,9 +1034,10 @@ class 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.
......
......@@ -1718,17 +1718,39 @@ class Simplify_thunk_traverse : public Traverse
{
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)
{
......@@ -1739,7 +1761,7 @@ 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;
}
......@@ -1761,13 +1783,23 @@ Gogo::simplify_thunk_statements()
// 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)
......@@ -2160,30 +2192,26 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
// 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.
......@@ -2191,30 +2219,17 @@ Thunk_statement::get_fn_and_arg(Translate_context* context, tree* pfn,
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.
......@@ -2230,38 +2245,20 @@ Statement::make_go_statement(Call_expression* call, source_location location)
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.
......
......@@ -852,7 +852,7 @@ class Thunk_statement : public 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
......@@ -868,8 +868,8 @@ class Thunk_statement : public Statement
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.
......
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