From patchwork Wed Apr 13 23:21:24 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Lance Taylor X-Patchwork-Id: 91143 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 4BE6F1007F1 for ; Thu, 14 Apr 2011 09:21:42 +1000 (EST) Received: (qmail 23231 invoked by alias); 13 Apr 2011 23:21:39 -0000 Received: (qmail 23219 invoked by uid 22791); 13 Apr 2011 23:21:36 -0000 X-SWARE-Spam-Status: No, hits=-2.2 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, SPF_HELO_PASS, TW_BF, TW_FN, TW_TM, T_RP_MATCHES_RCVD, T_TVD_MIME_NO_HEADERS X-Spam-Check-By: sourceware.org Received: from smtp-out.google.com (HELO smtp-out.google.com) (216.239.44.51) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 13 Apr 2011 23:21:30 +0000 Received: from kpbe15.cbf.corp.google.com (kpbe15.cbf.corp.google.com [172.25.105.79]) by smtp-out.google.com with ESMTP id p3DNLS0n000671 for ; Wed, 13 Apr 2011 16:21:29 -0700 Received: from ywj3 (ywj3.prod.google.com [10.192.10.3]) by kpbe15.cbf.corp.google.com with ESMTP id p3DNL43I024911 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for ; Wed, 13 Apr 2011 16:21:27 -0700 Received: by ywj3 with SMTP id 3so589404ywj.33 for ; Wed, 13 Apr 2011 16:21:27 -0700 (PDT) Received: by 10.101.53.17 with SMTP id f17mr23516ank.142.1302736887141; Wed, 13 Apr 2011 16:21:27 -0700 (PDT) Received: from coign.google.com (dhcp-172-22-127-165.mtv.corp.google.com [172.22.127.165]) by mx.google.com with ESMTPS id b3sm1021912ana.50.2011.04.13.16.21.25 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 13 Apr 2011 16:21:26 -0700 (PDT) From: Ian Lance Taylor To: gcc-patches@gcc.gnu.org, gofrontend-dev@googlegroups.com Subject: Go patch committed: Use backend interface for go and defer statements Date: Wed, 13 Apr 2011 16:21:24 -0700 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux) MIME-Version: 1.0 X-System-Of-Record: true X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This patch to the Go frontend uses the backend interface for go and defer statements. To make that work, I changed the defer stack variable from a tree to an Expression. This in turn required a bit of patching to ensure that the variable is created before it is used. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline. Ian 2011-04-13 Ian Lance Taylor * Make-lang.in (go/gogo-tree.o): depend on $(GO_RUNTIME_H). Index: gcc/go/Make-lang.in =================================================================== --- gcc/go/Make-lang.in (revision 172396) +++ gcc/go/Make-lang.in (working copy) @@ -262,7 +262,7 @@ go/go-dump.o: go/gofrontend/go-dump.cc $ 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) \ Index: gcc/go/gofrontend/gogo.cc =================================================================== --- gcc/go/gofrontend/gogo.cc (revision 172396) +++ gcc/go/gofrontend/gogo.cc (working copy) @@ -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 Index: gcc/go/gofrontend/gogo.h =================================================================== --- gcc/go/gofrontend/gogo.h (revision 172393) +++ gcc/go/gofrontend/gogo.h (working copy) @@ -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. Index: gcc/go/gofrontend/statements.cc =================================================================== --- gcc/go/gofrontend/statements.cc (revision 172396) +++ gcc/go/gofrontend/statements.cc (working copy) @@ -1718,17 +1718,39 @@ class Simplify_thunk_traverse : public T { 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, // 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(Translat 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); - } - - 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* fn; + Expression* arg; + if (!this->get_fn_and_arg(&fn, &arg)) + return error_mark_node; + + 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_expres 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; + 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); - - 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. Index: gcc/go/gofrontend/gogo-tree.cc =================================================================== --- gcc/go/gofrontend/gogo-tree.cc (revision 172393) +++ gcc/go/gofrontend/gogo-tree.cc (working copy) @@ -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_o // 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_o // 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 // 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 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 } } -// 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 Index: gcc/go/gofrontend/statements.h =================================================================== --- gcc/go/gofrontend/statements.h (revision 172393) +++ gcc/go/gofrontend/statements.h (working copy) @@ -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.