compiler: add slice initializers to the GC root list

As of https://golang.org/cl/32917 we can put slice initializers in the
    .data section.  The program can still change the values in those
    slices.  That means that if the slice elements can contain pointers,
    we need to register the entire initializer as a GC root.
    
    This would be straightforward except that we only have a Bexpression
    for the slice initializer, not an Expression.  So introduce a
    Backend_expression type that wraps a Bexpression as an Expression.
    
    The test case for this is https://golang.org/cl/33790.
    
    Reviewed-on: https://go-review.googlesource.com/33792

From-SVN: r243129
This commit is contained in:
Ian Lance Taylor 2016-12-01 19:54:36 +00:00
parent 0269650d4a
commit 98934fac3b
5 changed files with 128 additions and 5 deletions

View file

@ -1,4 +1,4 @@
97b949f249515a61d3c09e9e06f08c8af189e967
b7bad96ce0af50a1129eaab9aa110d68a601917b
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View file

@ -4295,6 +4295,20 @@ Unary_expression::do_get_backend(Translate_context* context)
true, copy_to_heap, false,
bexpr);
bexpr = gogo->backend()->var_expression(implicit, loc);
// If we are not copying a slice initializer to the heap,
// then it can be changed by the program, so if it can
// contain pointers we must register it as a GC root.
if (this->is_slice_init_
&& !copy_to_heap
&& this->expr_->type()->has_pointer())
{
Bexpression* root =
gogo->backend()->var_expression(implicit, loc);
root = gogo->backend()->address_expression(root, loc);
Type* type = Type::make_pointer_type(this->expr_->type());
gogo->add_gc_root(Expression::make_backend(root, type, loc));
}
}
else if ((this->expr_->is_composite_literal()
|| this->expr_->string_expression() != NULL)
@ -15433,6 +15447,28 @@ Expression::make_compound(Expression* init, Expression* expr, Location location)
return new Compound_expression(init, expr, location);
}
// Class Backend_expression.
int
Backend_expression::do_traverse(Traverse*)
{
return TRAVERSE_CONTINUE;
}
void
Backend_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << "backend_expression<";
ast_dump_context->dump_type(this->type_);
ast_dump_context->ostream() << ">";
}
Expression*
Expression::make_backend(Bexpression* bexpr, Type* type, Location location)
{
return new Backend_expression(bexpr, type, location);
}
// Import an expression. This comes at the end in order to see the
// various class definitions.

View file

@ -137,7 +137,8 @@ class Expression
EXPRESSION_STRUCT_FIELD_OFFSET,
EXPRESSION_LABEL_ADDR,
EXPRESSION_CONDITIONAL,
EXPRESSION_COMPOUND
EXPRESSION_COMPOUND,
EXPRESSION_BACKEND
};
Expression(Expression_classification, Location);
@ -485,6 +486,10 @@ class Expression
static Expression*
make_compound(Expression*, Expression*, Location);
// Make a backend expression.
static Expression*
make_backend(Bexpression*, Type*, Location);
// Return the expression classification.
Expression_classification
classification() const
@ -3825,6 +3830,54 @@ class Compound_expression : public Expression
Expression* expr_;
};
// A backend expression. This is a backend expression wrapped in an
// Expression, for convenience during backend generation.
class Backend_expression : public Expression
{
public:
Backend_expression(Bexpression* bexpr, Type* type, Location location)
: Expression(EXPRESSION_BACKEND, location), bexpr_(bexpr), type_(type)
{}
protected:
int
do_traverse(Traverse*);
// For now these are always valid static initializers. If that
// changes we can change this.
bool
do_is_static_initializer() const
{ return true; }
Type*
do_type()
{ return this->type_; }
void
do_determine_type(const Type_context*)
{ }
Expression*
do_copy()
{
return new Backend_expression(this->bexpr_, this->type_, this->location());
}
Bexpression*
do_get_backend(Translate_context*)
{ return this->bexpr_; }
void
do_dump_expression(Ast_dump_context*) const;
private:
// The backend expression we are wrapping.
Bexpression* bexpr_;
// The type of the expression;
Type* type_;
};
// A numeric constant. This is used both for untyped constants and
// for constants that have a type.

View file

@ -54,7 +54,9 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
interface_types_(),
specific_type_functions_(),
specific_type_functions_are_written_(false),
named_types_are_converted_(false)
named_types_are_converted_(false),
analysis_sets_(),
gc_roots_()
{
const Location loc = Linemap::predeclared_location();
@ -750,10 +752,9 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
Expression_list* roots_init = new Expression_list();
size_t i = 0;
for (std::vector<Named_object*>::const_iterator p = var_gc.begin();
p != var_gc.end();
++p, ++i)
++p)
{
Expression_list* init = new Expression_list();
@ -772,6 +773,27 @@ Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
roots_init->push_back(root_ctor);
}
for (std::vector<Expression*>::const_iterator p = this->gc_roots_.begin();
p != this->gc_roots_.end();
++p)
{
Expression_list *init = new Expression_list();
Expression* expr = *p;
Location eloc = expr->location();
init->push_back(expr);
Type* type = expr->type()->points_to();
go_assert(type != NULL);
Expression* size =
Expression::make_type_info(type, Expression::TYPE_INFO_SIZE);
init->push_back(size);
Expression* root_ctor =
Expression::make_struct_composite_literal(root_type, init, eloc);
roots_init->push_back(root_ctor);
}
// The list ends with a NULL entry.
Expression_list* null_init = new Expression_list();

View file

@ -19,6 +19,7 @@ class Typed_identifier;
class Typed_identifier_list;
class Function_type;
class Expression;
class Expression_list;
class Statement;
class Temporary_statement;
class Block;
@ -556,6 +557,15 @@ class Gogo
specific_type_functions_are_written() const
{ return this->specific_type_functions_are_written_; }
// Add a pointer that needs to be added to the list of objects
// traversed by the garbage collector. This should be an expression
// of pointer type that points to static storage. It's not
// necessary to add global variables to this list, just global
// variable initializers that would otherwise not be seen.
void
add_gc_root(Expression* expr)
{ this->gc_roots_.push_back(expr); }
// Traverse the tree. See the Traverse class.
void
traverse(Traverse*);
@ -892,6 +902,8 @@ class Gogo
// A list containing groups of possibly mutually recursive functions to be
// considered during escape analysis.
std::vector<Analysis_set> analysis_sets_;
// A list of objects to add to the GC roots.
std::vector<Expression*> gc_roots_;
};
// A block of statements.