diff mbox series

Go patch committed: keep variables captured by defer alive

Message ID CAOyqgcUhdWZ_Xj+CbtxbEdxNHjo8T3PeoxMXcptHaat2s2-3uQ@mail.gmail.com
State New
Headers show
Series Go patch committed: keep variables captured by defer alive | expand

Commit Message

Ian Lance Taylor Jan. 15, 2018, 7:13 p.m. UTC
Local variables captured by the deferred closure need to be live until
the function finishes, especially when the deferred function runs.
Function::build, for functions that have a defer, wraps the function
body in a try block.  So the backend sees the local variables only
live in the try block, without knowing that they are needed also in
the finally block where we invoke the deferred function.  This patch
to the Go frontend by Cherry Zhang fixes this by creating top-level
declarations for non-escaping address-taken locals when there is a
defer.  Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.
Committed to mainline.

Ian
diff mbox series

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 256655)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@ 
-4aa531c1765bba52848c6d71b9f57b593063d3ba
+afac7d7bed07ebe3add1784aaa9547c4d660d0ed
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 256593)
+++ gcc/go/gofrontend/gogo.cc	(working copy)
@@ -5568,6 +5568,7 @@  Function::build(Gogo* gogo, Named_object
   // initial values.
   std::vector<Bvariable*> vars;
   std::vector<Bexpression*> var_inits;
+  std::vector<Statement*> var_decls_stmts;
   for (Bindings::const_definitions_iterator p =
 	 this->block_->bindings()->begin_definitions();
        p != this->block_->bindings()->end_definitions();
@@ -5642,6 +5643,24 @@  Function::build(Gogo* gogo, Named_object
           vars.push_back(bvar);
           var_inits.push_back(init);
 	}
+      else if (this->defer_stack_ != NULL
+               && (*p)->is_variable()
+               && (*p)->var_value()->is_non_escaping_address_taken()
+               && !(*p)->var_value()->is_in_heap())
+        {
+          // Local variable captured by deferred closure needs to be live
+          // until the end of the function. We create a top-level
+          // declaration for it.
+          // TODO: we don't need to do this if the variable is not captured
+          // by the defer closure. There is no easy way to check it here,
+          // so we do this for all address-taken variables for now.
+          Variable* var = (*p)->var_value();
+          Temporary_statement* ts =
+            Statement::make_temporary(var->type(), NULL, var->location());
+          ts->set_is_address_taken();
+          var->set_toplevel_decl(ts);
+          var_decls_stmts.push_back(ts);
+        }
     }
   if (!gogo->backend()->function_set_parameters(this->fndecl_, param_vars))
     {
@@ -5661,7 +5680,7 @@  Function::build(Gogo* gogo, Named_object
     {
       // Declare variables if necessary.
       Bblock* var_decls = NULL;
-
+      std::vector<Bstatement*> var_decls_bstmt_list;
       Bstatement* defer_init = NULL;
       if (!vars.empty() || this->defer_stack_ != NULL)
 	{
@@ -5675,6 +5694,14 @@  Function::build(Gogo* gogo, Named_object
 	      Translate_context dcontext(gogo, named_function, this->block_,
                                          var_decls);
               defer_init = this->defer_stack_->get_backend(&dcontext);
+              var_decls_bstmt_list.push_back(defer_init);
+              for (std::vector<Statement*>::iterator p = var_decls_stmts.begin();
+                   p != var_decls_stmts.end();
+                   ++p)
+                {
+                  Bstatement* bstmt = (*p)->get_backend(&dcontext);
+                  var_decls_bstmt_list.push_back(bstmt);
+                }
 	    }
 	}
 
@@ -5693,8 +5720,6 @@  Function::build(Gogo* gogo, Named_object
                                               var_inits[i]);
           init.push_back(init_stmt);
 	}
-      if (defer_init != NULL)
-	init.push_back(defer_init);
       Bstatement* var_init = gogo->backend()->statement_list(init);
 
       // Initialize all variables before executing this code block.
@@ -5722,8 +5747,8 @@  Function::build(Gogo* gogo, Named_object
       // we built one.
       if (var_decls != NULL)
         {
-          std::vector<Bstatement*> code_stmt_list(1, code_stmt);
-          gogo->backend()->block_add_statements(var_decls, code_stmt_list);
+          var_decls_bstmt_list.push_back(code_stmt);
+          gogo->backend()->block_add_statements(var_decls, var_decls_bstmt_list);
           code_stmt = gogo->backend()->block_statement(var_decls);
         }