compiler: make top-level decl for address-taken non-escaping locals
authorCherry Zhang <cherryyz@google.com>
Tue, 9 Jan 2018 21:33:59 +0000 (21:33 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 9 Jan 2018 21:33:59 +0000 (21:33 +0000)
    If a local variable's address is taken and passed out of its
    lexical scope, GCC backend may reuse the stack slot for the
    variable, not knowing it is still live through a pointer. In
    this case, we create a top-level temporary variable and let the
    user-defined variable refer to the temporary variable as its
    storage location. As the temporary variable is declared at the
    top level, its stack slot will remain live throughout the
    function.

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

* go-gcc.cc (local_variable): Add decl_var parameter.

From-SVN: r256398

gcc/go/ChangeLog
gcc/go/go-gcc.cc
gcc/go/gofrontend/MERGE
gcc/go/gofrontend/backend.h
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h

index 4cd75f319ddf3af71fc2b00c616043e669b57242..ae6d000657366fb0906c09d214c84f333d049447 100644 (file)
@@ -1,3 +1,7 @@
+2018-01-09  Cherry Zhang  <cherryyz@google.com>
+
+       * go-gcc.cc (local_variable): Add decl_var parameter.
+
 2018-01-09  Cherry Zhang  <cherryyz@google.com>
 
        * lang.opt (fgo-debug-escape-hash): New option.
index 7c786025a38e92884378377d098154eeac2f358f..9bc049ebd9a549468208766e2849d2ed59f13e57 100644 (file)
@@ -426,7 +426,7 @@ class Gcc_backend : public Backend
   global_variable_set_init(Bvariable*, Bexpression*);
 
   Bvariable*
-  local_variable(Bfunction*, const std::string&, Btype*, bool,
+  local_variable(Bfunction*, const std::string&, Btype*, Bvariable*, bool,
                 Location);
 
   Bvariable*
@@ -2584,8 +2584,8 @@ Gcc_backend::global_variable_set_init(Bvariable* var, Bexpression* expr)
 
 Bvariable*
 Gcc_backend::local_variable(Bfunction* function, const std::string& name,
-                           Btype* btype, bool is_address_taken,
-                           Location location)
+                           Btype* btype, Bvariable* decl_var, 
+                           bool is_address_taken, Location location)
 {
   tree type_tree = btype->get_tree();
   if (type_tree == error_mark_node)
@@ -2597,6 +2597,11 @@ Gcc_backend::local_variable(Bfunction* function, const std::string& name,
   TREE_USED(decl) = 1;
   if (is_address_taken)
     TREE_ADDRESSABLE(decl) = 1;
+  if (decl_var != NULL)
+    {
+      DECL_HAS_VALUE_EXPR_P(decl) = 1;
+      SET_DECL_VALUE_EXPR(decl, decl_var->get_decl());
+    }
   go_preserve_from_gc(decl);
   return new Bvariable(decl);
 }
index 9ed20786abaf36cfe9b7852e4821eb5e67af036c..870348de6ef46c12720f9ea8590d3343b7d98846 100644 (file)
@@ -1,4 +1,4 @@
-0445dc01fd75325ff99f839cfaab29cb9f2a1f97
+29e821cf865aa6ee06cee9dae9823295202b1a61
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 601e7c95b3ae4588da6ff00eb008ebf28c419cc7..b6011053e695dfebea7eccd8fe954e67bd91e548 100644 (file)
@@ -516,15 +516,18 @@ class Backend
   // Create a local variable.  The frontend will create the local
   // variables first, and then create the block which contains them.
   // FUNCTION is the function in which the variable is defined.  NAME
-  // is the name of the variable.  TYPE is the type.  IS_ADDRESS_TAKEN
-  // is true if the address of this variable is taken (this implies
-  // that the address does not escape the function, as otherwise the
-  // variable would be on the heap).  LOCATION is where the variable
-  // is defined.  For each local variable the frontend will call
-  // init_statement to set the initial value.
+  // is the name of the variable.  TYPE is the type.  DECL_VAR, if not
+  // null, gives the location at which the value of this variable may
+  // be found, typically used to create an inner-scope reference to an
+  // outer-scope variable, to extend the lifetime of the variable beyond
+  // the inner scope.  IS_ADDRESS_TAKEN is true if the address of this
+  // variable is taken (this implies that the address does not escape
+  // the function, as otherwise the variable would be on the heap).
+  // LOCATION is where the variable is defined.  For each local variable
+  // the frontend will call init_statement to set the initial value.
   virtual Bvariable*
   local_variable(Bfunction* function, const std::string& name, Btype* type,
-                bool is_address_taken, Location location) = 0;
+                Bvariable* decl_var, bool is_address_taken, Location location) = 0;
 
   // Create a function parameter.  This is an incoming parameter, not
   // a result parameter (result parameters are treated as local
index b1c780794c905162e2badc5e60a8c8e07e8a8125..cdb8b184368f6f13173b7764fd7439da2a2c9dc8 100644 (file)
@@ -3876,6 +3876,29 @@ Flatten::variable(Named_object* no)
       return TRAVERSE_CONTINUE;
     }
 
+  if (!no->var_value()->is_parameter()
+      && !no->var_value()->is_receiver()
+      && !no->var_value()->is_closure()
+      && no->var_value()->is_non_escaping_address_taken()
+      && !no->var_value()->is_in_heap()
+      && no->var_value()->toplevel_decl() == NULL)
+    {
+      // Local variable that has address taken but not escape.
+      // It needs to be live beyond its lexical scope. So we
+      // create a top-level declaration for it.
+      // No need to do it if it is already in the top level.
+      Block* top_block = function_->func_value()->block();
+      if (top_block->bindings()->lookup_local(no->name()) != no)
+        {
+          Variable* var = no->var_value();
+          Temporary_statement* ts =
+            Statement::make_temporary(var->type(), NULL, var->location());
+          ts->set_is_address_taken();
+          top_block->add_statement_at_front(ts);
+          var->set_toplevel_decl(ts);
+        }
+    }
+
   go_assert(!no->var_value()->has_pre_init());
 
   return TRAVERSE_SKIP_COMPONENTS;
@@ -6174,7 +6197,8 @@ Variable::Variable(Type* type, Expression* init, bool is_global,
     type_from_init_tuple_(false), type_from_range_index_(false),
     type_from_range_value_(false), type_from_chan_element_(false),
     is_type_switch_var_(false), determined_type_(false),
-    in_unique_section_(false), escapes_(true)
+    in_unique_section_(false), escapes_(true),
+    toplevel_decl_(NULL)
 {
   go_assert(type != NULL || init != NULL);
   go_assert(!is_parameter || init == NULL);
@@ -6751,9 +6775,19 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
                                                   is_address_taken,
                                                   this->location_);
              else
-               bvar = backend->local_variable(bfunction, n, btype,
-                                              is_address_taken,
-                                              this->location_);
+                {
+                  Bvariable* bvar_decl = NULL;
+                  if (this->toplevel_decl_ != NULL)
+                    {
+                      Translate_context context(gogo, NULL, NULL, NULL);
+                      bvar_decl = this->toplevel_decl_->temporary_statement()
+                        ->get_backend_variable(&context);
+                    }
+                  bvar = backend->local_variable(bfunction, n, btype,
+                                                 bvar_decl,
+                                                 is_address_taken,
+                                                 this->location_);
+                }
            }
          this->backend_ = bvar;
        }
@@ -6785,7 +6819,7 @@ Result_variable::get_backend_variable(Gogo* gogo, Named_object* function,
          bool is_address_taken = (this->is_non_escaping_address_taken_
                                   && !this->is_in_heap());
          this->backend_ = backend->local_variable(bfunction, n, btype,
-                                                  is_address_taken,
+                                                  NULL, is_address_taken,
                                                   this->location_);
        }
     }
index ed044d413cfb7f8926731f4d17b1d7e74a6400df..65762727989d9a7fca83d2657335aeeed723de8a 100644 (file)
@@ -1884,6 +1884,20 @@ class Variable
     this->in_unique_section_ = true;
   }
 
+  // Return the top-level declaration for this variable.
+  Statement*
+  toplevel_decl()
+  { return this->toplevel_decl_; }
+
+  // Set the top-level declaration for this variable. Only used for local
+  // variables
+  void
+  set_toplevel_decl(Statement* s)
+  {
+    go_assert(!this->is_global_ && !this->is_parameter_ && !this->is_receiver_);
+    this->toplevel_decl_ = s;
+  }
+
   // Traverse the initializer expression.
   int
   traverse_expression(Traverse*, unsigned int traverse_mask);
@@ -1984,6 +1998,9 @@ class Variable
   // Whether this variable escapes the function it is created in.  This is
   // true until shown otherwise.
   bool escapes_ : 1;
+  // The top-level declaration for this variable. Only used for local
+  // variables. Must be a Temporary_statement if not NULL.
+  Statement* toplevel_decl_;
 };
 
 // A variable which is really the name for a function return value, or