Commit 81e0f092 by Cherry Zhang Committed by Ian Lance Taylor

compiler: make top-level decl for address-taken non-escaping locals

    
    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
parent 6ef72c36
2018-01-09 Cherry Zhang <cherryyz@google.com> 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. * lang.opt (fgo-debug-escape-hash): New option.
* go-c.h (struct go_create_gogo_args): Add debug_escape_hash * go-c.h (struct go_create_gogo_args): Add debug_escape_hash
field. field.
......
...@@ -426,7 +426,7 @@ class Gcc_backend : public Backend ...@@ -426,7 +426,7 @@ class Gcc_backend : public Backend
global_variable_set_init(Bvariable*, Bexpression*); global_variable_set_init(Bvariable*, Bexpression*);
Bvariable* Bvariable*
local_variable(Bfunction*, const std::string&, Btype*, bool, local_variable(Bfunction*, const std::string&, Btype*, Bvariable*, bool,
Location); Location);
Bvariable* Bvariable*
...@@ -2584,8 +2584,8 @@ Gcc_backend::global_variable_set_init(Bvariable* var, Bexpression* expr) ...@@ -2584,8 +2584,8 @@ Gcc_backend::global_variable_set_init(Bvariable* var, Bexpression* expr)
Bvariable* Bvariable*
Gcc_backend::local_variable(Bfunction* function, const std::string& name, Gcc_backend::local_variable(Bfunction* function, const std::string& name,
Btype* btype, bool is_address_taken, Btype* btype, Bvariable* decl_var,
Location location) bool is_address_taken, Location location)
{ {
tree type_tree = btype->get_tree(); tree type_tree = btype->get_tree();
if (type_tree == error_mark_node) if (type_tree == error_mark_node)
...@@ -2597,6 +2597,11 @@ Gcc_backend::local_variable(Bfunction* function, const std::string& name, ...@@ -2597,6 +2597,11 @@ Gcc_backend::local_variable(Bfunction* function, const std::string& name,
TREE_USED(decl) = 1; TREE_USED(decl) = 1;
if (is_address_taken) if (is_address_taken)
TREE_ADDRESSABLE(decl) = 1; 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); go_preserve_from_gc(decl);
return new Bvariable(decl); return new Bvariable(decl);
} }
......
0445dc01fd75325ff99f839cfaab29cb9f2a1f97 29e821cf865aa6ee06cee9dae9823295202b1a61
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -516,15 +516,18 @@ class Backend ...@@ -516,15 +516,18 @@ class Backend
// Create a local variable. The frontend will create the local // Create a local variable. The frontend will create the local
// variables first, and then create the block which contains them. // variables first, and then create the block which contains them.
// FUNCTION is the function in which the variable is defined. NAME // 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 the name of the variable. TYPE is the type. DECL_VAR, if not
// is true if the address of this variable is taken (this implies // null, gives the location at which the value of this variable may
// that the address does not escape the function, as otherwise the // be found, typically used to create an inner-scope reference to an
// variable would be on the heap). LOCATION is where the variable // outer-scope variable, to extend the lifetime of the variable beyond
// is defined. For each local variable the frontend will call // the inner scope. IS_ADDRESS_TAKEN is true if the address of this
// init_statement to set the initial value. // 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* virtual Bvariable*
local_variable(Bfunction* function, const std::string& name, Btype* type, 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 // Create a function parameter. This is an incoming parameter, not
// a result parameter (result parameters are treated as local // a result parameter (result parameters are treated as local
......
...@@ -3876,6 +3876,29 @@ Flatten::variable(Named_object* no) ...@@ -3876,6 +3876,29 @@ Flatten::variable(Named_object* no)
return TRAVERSE_CONTINUE; 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()); go_assert(!no->var_value()->has_pre_init());
return TRAVERSE_SKIP_COMPONENTS; return TRAVERSE_SKIP_COMPONENTS;
...@@ -6174,7 +6197,8 @@ Variable::Variable(Type* type, Expression* init, bool is_global, ...@@ -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_init_tuple_(false), type_from_range_index_(false),
type_from_range_value_(false), type_from_chan_element_(false), type_from_range_value_(false), type_from_chan_element_(false),
is_type_switch_var_(false), determined_type_(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(type != NULL || init != NULL);
go_assert(!is_parameter || init == NULL); go_assert(!is_parameter || init == NULL);
...@@ -6751,10 +6775,20 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function, ...@@ -6751,10 +6775,20 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
is_address_taken, is_address_taken,
this->location_); this->location_);
else else
{
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 = backend->local_variable(bfunction, n, btype,
bvar_decl,
is_address_taken, is_address_taken,
this->location_); this->location_);
} }
}
this->backend_ = bvar; this->backend_ = bvar;
} }
} }
...@@ -6785,7 +6819,7 @@ Result_variable::get_backend_variable(Gogo* gogo, Named_object* function, ...@@ -6785,7 +6819,7 @@ Result_variable::get_backend_variable(Gogo* gogo, Named_object* function,
bool is_address_taken = (this->is_non_escaping_address_taken_ bool is_address_taken = (this->is_non_escaping_address_taken_
&& !this->is_in_heap()); && !this->is_in_heap());
this->backend_ = backend->local_variable(bfunction, n, btype, this->backend_ = backend->local_variable(bfunction, n, btype,
is_address_taken, NULL, is_address_taken,
this->location_); this->location_);
} }
} }
......
...@@ -1884,6 +1884,20 @@ class Variable ...@@ -1884,6 +1884,20 @@ class Variable
this->in_unique_section_ = true; 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. // Traverse the initializer expression.
int int
traverse_expression(Traverse*, unsigned int traverse_mask); traverse_expression(Traverse*, unsigned int traverse_mask);
...@@ -1984,6 +1998,9 @@ class Variable ...@@ -1984,6 +1998,9 @@ class Variable
// Whether this variable escapes the function it is created in. This is // Whether this variable escapes the function it is created in. This is
// true until shown otherwise. // true until shown otherwise.
bool escapes_ : 1; 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 // A variable which is really the name for a function return value, or
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment