This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Go patch committed: Use backend interface for go and defer statements


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  <iant@google.com>

	* 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.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]