compiler: improve escape analysis on interface conversions

If an interface does not escape, it doesn't need a heap
    allocation to hold the data (for non-direct interface type).
    This CL improves the escape analysis to track interface
    conversions, and reduces these allocations.
    
    Implicit interface conversions were mostly added late in the
    compilation pipeline, after the escape analysis. For the escape
    analysis to see them, we move the introduction of these
    conversions earlier, right before the escape analysis.
    
    Now that the compiler can generate interface conversions inlined,
    gcc/testsuite/go.test/test/nilptr2.go needs to be adjusted as in
    golang.org/cl/176579, so the use function does an actual use.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/176459

	* go.test/test/nilptr2.go: Change use function to actually do
	something.

From-SVN: r271276
This commit is contained in:
Cherry Zhang 2019-05-16 04:35:15 +00:00 committed by Ian Lance Taylor
parent 92b8603c7c
commit e8e91b8454
11 changed files with 369 additions and 23 deletions

View file

@ -1,4 +1,4 @@
6112f9b8fa9d57d2db8a709cc8b44a94d778d08a
2df0879e7880057293c0a59be6868a3e6ea5105b
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View file

@ -2407,9 +2407,11 @@ Escape_analysis_assign::assign(Node* dst, Node* src)
Type* tt = tce->type();
if ((ft->is_string_type() && tt->is_slice_type())
|| (ft->is_slice_type() && tt->is_string_type())
|| (ft->integer_type() != NULL && tt->is_string_type()))
|| (ft->integer_type() != NULL && tt->is_string_type())
|| tt->interface_type() != NULL)
{
// string([]byte), string([]rune), []byte(string), []rune(string), string(rune)
// string([]byte), string([]rune), []byte(string), []rune(string), string(rune),
// interface(T)
this->flows(dst, src);
break;
}
@ -3151,14 +3153,24 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
Type* tt = tce->type();
if ((ft->is_string_type() && tt->is_slice_type())
|| (ft->is_slice_type() && tt->is_string_type())
|| (ft->integer_type() != NULL && tt->is_string_type()))
|| (ft->integer_type() != NULL && tt->is_string_type())
|| tt->interface_type() != NULL)
{
// string([]byte), string([]rune), []byte(string), []rune(string), string(rune)
// string([]byte), string([]rune), []byte(string), []rune(string), string(rune),
// interface(T)
src->set_encoding(Node::ESCAPE_HEAP);
if (debug_level != 0 && osrcesc != src->encoding())
go_inform(src->location(), "%s escapes to heap",
src->ast_format(gogo).c_str());
extra_loop_depth = mod_loop_depth;
if (tt->interface_type() != NULL
&& ft->has_pointer()
&& !ft->is_direct_iface_type())
// We're converting from a non-direct interface type.
// The interface will hold a heap copy of the data
// Flow the data to heap. See issue 29353.
this->flood(level, this->context_->sink(),
Node::make_node(tce->expr()), -1);
}
}
else if (e->array_index_expression() != NULL

View file

@ -184,11 +184,10 @@ Expression::convert_for_assignment(Gogo*, Type* lhs_type,
NULL);
if (!are_identical && lhs_type->interface_type() != NULL)
{
if (rhs_type->interface_type() == NULL)
return Expression::convert_type_to_interface(lhs_type, rhs, location);
else
return Expression::convert_interface_to_interface(lhs_type, rhs, false,
location);
// Type to interface conversions have been made explicit early.
go_assert(rhs_type->interface_type() != NULL);
return Expression::convert_interface_to_interface(lhs_type, rhs, false,
location);
}
else if (!are_identical && rhs_type->interface_type() != NULL)
return Expression::convert_interface_to_type(lhs_type, rhs, location);
@ -231,11 +230,12 @@ Expression::convert_for_assignment(Gogo*, Type* lhs_type,
}
// Return an expression for a conversion from a non-interface type to an
// interface type.
// interface type. If ON_STACK is true, it can allocate the storage on
// stack.
Expression*
Expression::convert_type_to_interface(Type* lhs_type, Expression* rhs,
Location location)
bool on_stack, Location location)
{
Interface_type* lhs_interface_type = lhs_type->interface_type();
bool lhs_is_empty = lhs_interface_type->is_empty();
@ -302,9 +302,9 @@ Expression::convert_type_to_interface(Type* lhs_type, Expression* rhs,
{
// We are assigning a non-pointer value to the interface; the
// interface gets a copy of the value in the heap if it escapes.
// TODO(cmang): Associate escape state state of RHS with newly
// created OBJ.
obj = Expression::make_heap_expression(rhs, location);
if (on_stack)
obj->heap_expression()->set_allocate_on_stack();
}
return Expression::make_interface_value(lhs_type, first_field, obj, location);
@ -3625,6 +3625,14 @@ Type_conversion_expression::do_flatten(Gogo*, Named_object*,
inserter->insert(temp);
this->expr_ = Expression::make_temporary_reference(temp, this->location());
}
// For interface conversion, decide if we can allocate on stack.
if (this->type()->interface_type() != NULL)
{
Node* n = Node::make_node(this);
if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
this->no_escape_ = true;
}
return this;
}
@ -3812,12 +3820,20 @@ Type_conversion_expression::do_get_backend(Translate_context* context)
Bexpression* bexpr = this->expr_->get_backend(context);
return gogo->backend()->convert_expression(btype, bexpr, loc);
}
else if (type->interface_type() != NULL
&& expr_type->interface_type() == NULL)
{
Expression* conversion =
Expression::convert_type_to_interface(type, this->expr_,
this->no_escape_, loc);
return conversion->get_backend(context);
}
else if (type->interface_type() != NULL
|| expr_type->interface_type() != NULL)
{
Expression* conversion =
Expression::convert_for_assignment(gogo, type, this->expr_,
this->location());
loc);
return conversion->get_backend(context);
}
else if (type->is_string_type()
@ -8466,6 +8482,10 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
lhs->array_index_expression()->set_needs_bounds_check(false);
gogo->lower_expression(function, inserter, &lhs);
gogo->flatten_expression(function, inserter, &lhs);
Expression* elem = *pa;
if (!Type::are_identical(element_type, elem->type(), 0, NULL)
&& element_type->interface_type() != NULL)
elem = Expression::make_cast(element_type, elem, loc);
// The flatten pass runs after the write barrier pass, so we
// need to insert a write barrier here if necessary.
// However, if ASSIGN_LHS is not NULL, we have been called
@ -8473,12 +8493,12 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
Statement* assign;
if (assign_lhs != NULL
|| !gogo->assign_needs_write_barrier(lhs))
assign = Statement::make_assignment(lhs, *pa, loc);
assign = Statement::make_assignment(lhs, elem, loc);
else
{
Function* f = function == NULL ? NULL : function->func_value();
assign = gogo->assign_with_write_barrier(f, NULL, inserter,
lhs, *pa, loc);
lhs, elem, loc);
}
inserter->insert(assign);
}
@ -9840,7 +9860,7 @@ Builtin_call_expression::do_get_backend(Translate_context* context)
Type::make_empty_interface_type(Linemap::predeclared_location());
Expression* nil = Expression::make_nil(location);
nil = Expression::convert_for_assignment(gogo, empty, nil, location);
nil = Expression::make_interface_value(empty, nil, nil, location);
// We need to handle a deferred call to recover specially,
// because it changes whether it can recover a panic or not.
@ -10474,6 +10494,46 @@ Call_expression::do_flatten(Gogo* gogo, Named_object*,
return this;
}
// Make implicit type conversions explicit.
void
Call_expression::do_add_conversions()
{
// Skip call that requires a thunk. We generate conversions inside the thunk.
if (this->is_concurrent_ || this->is_deferred_)
return;
if (this->args_ == NULL || this->args_->empty())
return;
Function_type* fntype = this->get_function_type();
if (fntype == NULL)
{
go_assert(saw_errors());
return;
}
if (fntype->parameters() == NULL || fntype->parameters()->empty())
return;
Location loc = this->location();
Expression_list::iterator pa = this->args_->begin();
Typed_identifier_list::const_iterator pp = fntype->parameters()->begin();
bool is_interface_method =
this->fn_->interface_field_reference_expression() != NULL;
if (!is_interface_method && fntype->is_method())
{
// Skip the receiver argument, which cannot be interface.
pa++;
}
for (; pa != this->args_->end(); ++pa, ++pp)
{
Type* pt = pp->type();
if (!Type::are_identical(pt, (*pa)->type(), 0, NULL)
&& pt->interface_type() != NULL)
*pa = Expression::make_cast(pt, *pa, loc);
}
}
// Get the function type. This can return NULL in error cases.
Function_type*
@ -12250,6 +12310,21 @@ Map_index_expression::do_check_types(Gogo*)
}
}
// Add explicit type conversions.
void
Map_index_expression::do_add_conversions()
{
Map_type* mt = this->get_map_type();
if (mt == NULL)
return;
Type* lt = mt->key_type();
Type* rt = this->index_->type();
if (!Type::are_identical(lt, rt, 0, NULL)
&& lt->interface_type() != NULL)
this->index_ = Expression::make_cast(lt, this->index_, this->location());
}
// Get the backend representation for a map index.
Bexpression*
@ -13450,6 +13525,33 @@ Struct_construction_expression::do_flatten(Gogo*, Named_object*,
return this;
}
// Make implicit type conversions explicit.
void
Struct_construction_expression::do_add_conversions()
{
if (this->vals() == NULL)
return;
Location loc = this->location();
const Struct_field_list* fields = this->type_->struct_type()->fields();
Expression_list::iterator pv = this->vals()->begin();
for (Struct_field_list::const_iterator pf = fields->begin();
pf != fields->end();
++pf, ++pv)
{
if (pv == this->vals()->end())
break;
if (*pv != NULL)
{
Type* ft = pf->type();
if (!Type::are_identical(ft, (*pv)->type(), 0, NULL)
&& ft->interface_type() != NULL)
*pv = Expression::make_cast(ft, *pv, loc);
}
}
}
// Return the backend representation for constructing a struct.
Bexpression*
@ -13698,6 +13800,26 @@ Array_construction_expression::do_flatten(Gogo*, Named_object*,
return this;
}
// Make implicit type conversions explicit.
void
Array_construction_expression::do_add_conversions()
{
if (this->vals() == NULL)
return;
Type* et = this->type_->array_type()->element_type();
if (et->interface_type() == NULL)
return;
Location loc = this->location();
for (Expression_list::iterator pv = this->vals()->begin();
pv != this->vals()->end();
++pv)
if (!Type::are_identical(et, (*pv)->type(), 0, NULL))
*pv = Expression::make_cast(et, *pv, loc);
}
// Get a constructor expression for the array values.
Bexpression*
@ -14221,6 +14343,37 @@ Map_construction_expression::do_copy()
this->location());
}
// Make implicit type conversions explicit.
void
Map_construction_expression::do_add_conversions()
{
if (this->vals_ == NULL || this->vals_->empty())
return;
Map_type* mt = this->type_->map_type();
Type* kt = mt->key_type();
Type* vt = mt->val_type();
bool key_is_interface = (kt->interface_type() != NULL);
bool val_is_interface = (vt->interface_type() != NULL);
if (!key_is_interface && !val_is_interface)
return;
Location loc = this->location();
for (Expression_list::iterator pv = this->vals_->begin();
pv != this->vals_->end();
++pv)
{
if (key_is_interface &&
!Type::are_identical(kt, (*pv)->type(), 0, NULL))
*pv = Expression::make_cast(kt, *pv, loc);
++pv;
if (val_is_interface &&
!Type::are_identical(vt, (*pv)->type(), 0, NULL))
*pv = Expression::make_cast(vt, *pv, loc);
}
}
// Return the backend representation for constructing a map.
Bexpression*

View file

@ -934,6 +934,11 @@ class Expression
flatten(Gogo* gogo, Named_object* function, Statement_inserter* inserter)
{ return this->do_flatten(gogo, function, inserter); }
// Make implicit type conversions explicit.
void
add_conversions()
{ this->do_add_conversions(); }
// Determine the real type of an expression with abstract integer,
// floating point, or complex type. TYPE_CONTEXT describes the
// expected type.
@ -1019,6 +1024,13 @@ class Expression
Expression* rhs, bool for_type_guard,
Location);
// Return an expression for a conversion from a non-interface type to an
// interface type. If ON_STACK is true, it can allocate the storage on
// stack.
static Expression*
convert_type_to_interface(Type* lhs_type, Expression* rhs,
bool on_stack, Location);
// Return a backend expression implementing the comparison LEFT OP RIGHT.
// TYPE is the type of both sides.
static Bexpression*
@ -1070,6 +1082,10 @@ class Expression
do_flatten(Gogo*, Named_object*, Statement_inserter*)
{ return this; }
// Make implicit type conversions explicit.
virtual void
do_add_conversions()
{ }
// Return whether this is a constant expression.
virtual bool
@ -1214,9 +1230,6 @@ class Expression
: NULL);
}
static Expression*
convert_type_to_interface(Type*, Expression*, Location);
static Expression*
unpack_direct_iface(Expression*, Location);
@ -1674,7 +1687,7 @@ class Type_conversion_expression : public Expression
Location location)
: Expression(EXPRESSION_CONVERSION, location),
type_(type), expr_(expr), may_convert_function_types_(false),
no_copy_(false)
no_copy_(false), no_escape_(false)
{ }
// Return the type to which we are converting.
@ -1766,6 +1779,10 @@ class Type_conversion_expression : public Expression
// True if a string([]byte) conversion can reuse the backing store
// without copying. Only used in string([]byte) conversion.
bool no_copy_;
// True if a conversion to interface does not escape, so it does
// not need a heap allocation. Only used in type-to-interface
// conversion.
bool no_escape_;
};
// An unsafe type conversion, used to pass values to builtin functions.
@ -2402,6 +2419,9 @@ class Call_expression : public Expression
void
do_dump_expression(Ast_dump_context*) const;
void
do_add_conversions();
private:
bool
check_argument_type(int, const Type*, const Type*, Location, bool);
@ -3162,6 +3182,9 @@ class Map_index_expression : public Expression
void
do_dump_expression(Ast_dump_context*) const;
void
do_add_conversions();
private:
// The map we are looking into.
Expression* map_;
@ -3648,6 +3671,9 @@ class Struct_construction_expression : public Expression,
void
do_dump_expression(Ast_dump_context*) const;
void
do_add_conversions();
private:
// The type of the struct to construct.
Type* type_;
@ -3721,6 +3747,9 @@ protected:
virtual void
dump_slice_storage_expression(Ast_dump_context*) const { }
void
do_add_conversions();
private:
// The type of the array to construct.
Type* type_;
@ -3844,6 +3873,9 @@ class Map_construction_expression : public Expression
void
do_dump_expression(Ast_dump_context*) const;
void
do_add_conversions();
private:
// The type of the map to construct.
Type* type_;

View file

@ -142,6 +142,10 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
if (only_check_syntax)
return;
// Make implicit type conversions explicit.
::gogo->add_conversions();
// Analyze the program flow for escape information.
::gogo->analyze_escape();
// Export global identifiers as appropriate.

View file

@ -2996,6 +2996,57 @@ Gogo::lower_constant(Named_object* no)
lower.constant(no, false);
}
// Make implicit type conversions explicit. Currently only does for
// interface conversions, so the escape analysis can see them and
// optimize.
class Add_conversions : public Traverse
{
public:
Add_conversions()
: Traverse(traverse_statements
| traverse_expressions)
{ }
int
statement(Block*, size_t* pindex, Statement*);
int
expression(Expression**);
};
// Add explicit conversions in a statement.
int
Add_conversions::statement(Block*, size_t*, Statement* sorig)
{
sorig->add_conversions();
return TRAVERSE_CONTINUE;
}
// Add explicit conversions in an expression.
int
Add_conversions::expression(Expression** pexpr)
{
(*pexpr)->add_conversions();
return TRAVERSE_CONTINUE;
}
void
Gogo::add_conversions()
{
Add_conversions add_conversions;
this->traverse(&add_conversions);
}
void
Gogo::add_conversions_in_block(Block *b)
{
Add_conversions add_conversions;
b->traverse(&add_conversions);
}
// Traverse the tree to create function descriptors as needed.
class Create_function_descriptors : public Traverse

View file

@ -683,6 +683,14 @@ class Gogo
void
check_return_statements();
// Make implicit type conversions explicit.
void
add_conversions();
// Make implicit type conversions explicit in a block.
void
add_conversions_in_block(Block*);
// Analyze the program flow for escape information.
void
analyze_escape();

View file

@ -324,6 +324,22 @@ Variable_declaration_statement::do_flatten(Gogo* gogo, Named_object* function,
return this;
}
// Add explicit type conversions.
void
Variable_declaration_statement::do_add_conversions()
{
Variable* var = this->var_->var_value();
Expression* init = var->init();
if (init == NULL)
return;
Type* lt = var->type();
Type* rt = init->type();
if (!Type::are_identical(lt, rt, 0, NULL)
&& lt->interface_type() != NULL)
var->set_init(Expression::make_cast(lt, init, this->location()));
}
// Convert a variable declaration to the backend representation.
Bstatement*
@ -582,6 +598,20 @@ Temporary_statement::do_flatten(Gogo*, Named_object*, Block*,
return this;
}
// Add explicit type conversions.
void
Temporary_statement::do_add_conversions()
{
if (this->init_ == NULL)
return;
Type* lt = this->type();
Type* rt = this->init_->type();
if (!Type::are_identical(lt, rt, 0, NULL)
&& lt->interface_type() != NULL)
this->init_ = Expression::make_cast(lt, this->init_, this->location());
}
// Convert to backend representation.
Bstatement*
@ -960,6 +990,18 @@ Assignment_statement::do_flatten(Gogo*, Named_object*, Block*,
return this;
}
// Add explicit type conversions.
void
Assignment_statement::do_add_conversions()
{
Type* lt = this->lhs_->type();
Type* rt = this->rhs_->type();
if (!Type::are_identical(lt, rt, 0, NULL)
&& lt->interface_type() != NULL)
this->rhs_ = Expression::make_cast(lt, this->rhs_, this->location());
}
// Convert an assignment statement to the backend representation.
Bstatement*
@ -2638,6 +2680,8 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name)
// just for the call statement now. The other types are known.
call_statement->determine_types();
gogo->add_conversions_in_block(b);
gogo->flatten_block(function, b);
if (may_call_recover
@ -4543,6 +4587,18 @@ Send_statement::do_flatten(Gogo*, Named_object*, Block*,
return this;
}
// Add explicit type conversions.
void
Send_statement::do_add_conversions()
{
Type* lt = this->channel_->type()->channel_type()->element_type();
Type* rt = this->val_->type();
if (!Type::are_identical(lt, rt, 0, NULL)
&& lt->interface_type() != NULL)
this->val_ = Expression::make_cast(lt, this->val_, this->location());
}
// Convert a send statement to the backend representation.
Bstatement*

View file

@ -338,6 +338,11 @@ class Statement
export_statement(Export_function_body* efb)
{ this->do_export_statement(efb); }
// Make implicit type conversions explicit.
void
add_conversions()
{ this->do_add_conversions(); }
// Read a statement from export data. The location should be used
// for the returned statement. Errors should be reported using the
// Import_function_body's location method.
@ -534,6 +539,11 @@ class Statement
virtual void
do_dump_statement(Ast_dump_context*) const = 0;
// Implemented by child class: make implicit conversions explicit.
virtual void
do_add_conversions()
{ }
// Traverse an expression in a statement.
int
traverse_expression(Traverse*, Expression**);
@ -645,6 +655,9 @@ class Assignment_statement : public Statement
void
do_dump_statement(Ast_dump_context*) const;
void
do_add_conversions();
private:
// Left hand side--the lvalue.
Expression* lhs_;
@ -717,6 +730,9 @@ class Temporary_statement : public Statement
void
do_dump_statement(Ast_dump_context*) const;
void
do_add_conversions();
private:
// The type of the temporary variable.
Type* type_;
@ -774,6 +790,9 @@ class Variable_declaration_statement : public Statement
void
do_dump_statement(Ast_dump_context*) const;
void
do_add_conversions();
private:
Named_object* var_;
};
@ -959,6 +978,9 @@ class Send_statement : public Statement
void
do_dump_statement(Ast_dump_context*) const;
void
do_add_conversions();
private:
// The channel on which to send the value.
Expression* channel_;

View file

@ -1,3 +1,8 @@
2019-05-15 Cherry Zhang <cherryyz@google.com>
* go.test/test/nilptr2.go: Change use function to actually do
something.
2019-05-16 Jakub Jelinek <jakub@redhat.com>
PR middle-end/90478

View file

@ -35,7 +35,10 @@ var m *M
var m1 *M1
var m2 *M2
func use(interface{}) {
var V interface{}
func use(x interface{}) {
V = x
}
var tests = []struct{