compiler: make sure variables captured by defer closure live
authorIan Lance Taylor <ian@gcc.gnu.org>
Mon, 15 Jan 2018 19:13:47 +0000 (19:13 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Mon, 15 Jan 2018 19:13:47 +0000 (19:13 +0000)
    Local variables captured by the deferred closure need to be live
    until the function finishes, especially when the deferred
    function runs. In Function::build, for function that has a defer,
    we wrap 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. Fix this by creating top-level
    declarations for non-escaping address-taken locals when there
    is a defer.

    An example of miscompilation without this CL:

    func F(fn func()) {
            didPanic := true
            defer func() {
                    println(didPanic)
            }()
            fn()
            didPanic = false
    }

    With escape analysis turned on, at optimization level -O1 or -O2,
    the store "didPanic = false" is elided by the backend's
    optimizer, presumably because it thinks "didPanic" is not live
    after the store, so the store is useless.

    Reviewed-on: https://go-review.googlesource.com/86241

From-SVN: r256706

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/gogo.cc

index 7d87aa8ab53de07d49b0cfb6dc7e524861159329..8f5e4ba6866f9491cc30b559623eb6e6f2a67a59 100644 (file)
@@ -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 579b8a3ee5e3815541e0db1e320a134fe6c882fe..e8bc7aea9bfca19f64472d5ea1ad43213edb5a85 100644 (file)
@@ -5568,6 +5568,7 @@ Function::build(Gogo* gogo, Named_object* named_function)
   // 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* named_function)
           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* named_function)
     {
       // 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* named_function)
              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* named_function)
                                               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* named_function)
       // 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);
         }