compiler: move slice construction to callers of makeslice
This is the gccgo version of https://golang.org/cl/141822: Only return a pointer p to the new slices backing array from makeslice. Makeslice callers then construct sliceheader{p, len, cap} explictly instead of makeslice returning the slice. This change caused the GCC backend to break the runtime/pprof test by merging together the identical functions allocateReflectTransient and allocateTransient2M. This caused the traceback to be other than expected. Fix that by making the functions not identical. This is a step toward updating libgo to the Go1.12beta1 release. Reviewed-on: https://go-review.googlesource.com/c/155937 From-SVN: r267660
This commit is contained in:
parent
575eb8f58b
commit
fdcef314bc
8 changed files with 166 additions and 92 deletions
|
@ -1,4 +1,4 @@
|
|||
c257303eaef143663216e483857d5b259e05753f
|
||||
c8a9bccbc524381d150c84907a61ac257c1b07cc
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
|
|
@ -1737,6 +1737,16 @@ Escape_analysis_assign::expression(Expression** pexpr)
|
|||
}
|
||||
break;
|
||||
|
||||
case Expression::EXPRESSION_SLICE_VALUE:
|
||||
{
|
||||
// Connect the pointer field to the slice value.
|
||||
Node* slice_node = Node::make_node(*pexpr);
|
||||
Node* ptr_node =
|
||||
Node::make_node((*pexpr)->slice_value_expression()->valmem());
|
||||
this->assign(slice_node, ptr_node);
|
||||
}
|
||||
break;
|
||||
|
||||
case Expression::EXPRESSION_HEAP:
|
||||
{
|
||||
Node* pointer_node = Node::make_node(*pexpr);
|
||||
|
@ -2263,6 +2273,8 @@ Escape_analysis_assign::assign(Node* dst, Node* src)
|
|||
// DST = map[T]V{...}.
|
||||
case Expression::EXPRESSION_STRUCT_CONSTRUCTION:
|
||||
// DST = T{...}.
|
||||
case Expression::EXPRESSION_SLICE_VALUE:
|
||||
// DST = slice{ptr, len, cap}
|
||||
case Expression::EXPRESSION_ALLOCATION:
|
||||
// DST = new(T).
|
||||
case Expression::EXPRESSION_BOUND_METHOD:
|
||||
|
|
|
@ -7787,21 +7787,29 @@ Builtin_call_expression::lower_make(Statement_inserter* inserter)
|
|||
Expression* call;
|
||||
if (is_slice)
|
||||
{
|
||||
Temporary_statement* len_temp = NULL;
|
||||
if (!len_arg->is_constant())
|
||||
{
|
||||
len_temp = Statement::make_temporary(NULL, len_arg, loc);
|
||||
inserter->insert(len_temp);
|
||||
len_arg = Expression::make_temporary_reference(len_temp, loc);
|
||||
}
|
||||
|
||||
if (cap_arg == NULL)
|
||||
{
|
||||
cap_small = len_small;
|
||||
if (len_arg->numeric_constant_value(&nclen)
|
||||
&& nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID)
|
||||
cap_arg = Expression::make_integer_ul(vlen, len_arg->type(), loc);
|
||||
else
|
||||
{
|
||||
Temporary_statement* temp = Statement::make_temporary(NULL,
|
||||
len_arg,
|
||||
loc);
|
||||
inserter->insert(temp);
|
||||
len_arg = Expression::make_temporary_reference(temp, loc);
|
||||
cap_arg = Expression::make_temporary_reference(temp, loc);
|
||||
}
|
||||
if (len_temp == NULL)
|
||||
cap_arg = len_arg->copy();
|
||||
else
|
||||
cap_arg = Expression::make_temporary_reference(len_temp, loc);
|
||||
}
|
||||
else if (!cap_arg->is_constant())
|
||||
{
|
||||
Temporary_statement* cap_temp = Statement::make_temporary(NULL,
|
||||
cap_arg,
|
||||
loc);
|
||||
inserter->insert(cap_temp);
|
||||
cap_arg = Expression::make_temporary_reference(cap_temp, loc);
|
||||
}
|
||||
|
||||
Type* et = type->array_type()->element_type();
|
||||
|
@ -7809,7 +7817,12 @@ Builtin_call_expression::lower_make(Statement_inserter* inserter)
|
|||
Runtime::Function code = Runtime::MAKESLICE;
|
||||
if (!len_small || !cap_small)
|
||||
code = Runtime::MAKESLICE64;
|
||||
call = Runtime::make_call(code, loc, 3, type_arg, len_arg, cap_arg);
|
||||
Expression* mem = Runtime::make_call(code, loc, 3, type_arg, len_arg,
|
||||
cap_arg);
|
||||
mem = Expression::make_unsafe_cast(Type::make_pointer_type(et), mem,
|
||||
loc);
|
||||
call = Expression::make_slice_value(type, mem, len_arg->copy(),
|
||||
cap_arg->copy(), loc);
|
||||
}
|
||||
else if (is_map)
|
||||
{
|
||||
|
@ -13585,9 +13598,13 @@ Slice_construction_expression::do_get_backend(Translate_context* context)
|
|||
go_assert(this->storage_escapes_ || this->element_count() == 0);
|
||||
space = Expression::make_heap_expression(this->array_val_, loc);
|
||||
}
|
||||
Array_type* at = this->valtype_->array_type();
|
||||
Type* et = at->element_type();
|
||||
space = Expression::make_unsafe_cast(Type::make_pointer_type(et),
|
||||
space, loc);
|
||||
|
||||
// Build a constructor for the slice.
|
||||
Expression* len = this->valtype_->array_type()->length();
|
||||
Expression* len = at->length();
|
||||
Expression* slice_val =
|
||||
Expression::make_slice_value(this->type(), space, len, len, loc);
|
||||
return slice_val->get_backend(context);
|
||||
|
@ -15354,72 +15371,33 @@ Expression::make_slice_info(Expression* slice, Slice_info slice_info,
|
|||
return new Slice_info_expression(slice, slice_info, location);
|
||||
}
|
||||
|
||||
// An expression that represents a slice value: a struct with value pointer,
|
||||
// length, and capacity fields.
|
||||
|
||||
class Slice_value_expression : public Expression
|
||||
{
|
||||
public:
|
||||
Slice_value_expression(Type* type, Expression* valptr, Expression* len,
|
||||
Expression* cap, Location location)
|
||||
: Expression(EXPRESSION_SLICE_VALUE, location),
|
||||
type_(type), valptr_(valptr), len_(len), cap_(cap)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
int
|
||||
do_traverse(Traverse*);
|
||||
|
||||
Type*
|
||||
do_type()
|
||||
{ return this->type_; }
|
||||
|
||||
void
|
||||
do_determine_type(const Type_context*)
|
||||
{ go_unreachable(); }
|
||||
|
||||
Expression*
|
||||
do_copy()
|
||||
{
|
||||
return new Slice_value_expression(this->type_->copy_expressions(),
|
||||
this->valptr_->copy(),
|
||||
this->len_->copy(), this->cap_->copy(),
|
||||
this->location());
|
||||
}
|
||||
|
||||
Bexpression*
|
||||
do_get_backend(Translate_context* context);
|
||||
|
||||
void
|
||||
do_dump_expression(Ast_dump_context*) const;
|
||||
|
||||
private:
|
||||
// The type of the slice value.
|
||||
Type* type_;
|
||||
// The pointer to the values in the slice.
|
||||
Expression* valptr_;
|
||||
// The length of the slice.
|
||||
Expression* len_;
|
||||
// The capacity of the slice.
|
||||
Expression* cap_;
|
||||
};
|
||||
// Class Slice_value_expression.
|
||||
|
||||
int
|
||||
Slice_value_expression::do_traverse(Traverse* traverse)
|
||||
{
|
||||
if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT
|
||||
|| Expression::traverse(&this->valptr_, traverse) == TRAVERSE_EXIT
|
||||
|| Expression::traverse(&this->valmem_, traverse) == TRAVERSE_EXIT
|
||||
|| Expression::traverse(&this->len_, traverse) == TRAVERSE_EXIT
|
||||
|| Expression::traverse(&this->cap_, traverse) == TRAVERSE_EXIT)
|
||||
return TRAVERSE_EXIT;
|
||||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
Expression*
|
||||
Slice_value_expression::do_copy()
|
||||
{
|
||||
return new Slice_value_expression(this->type_->copy_expressions(),
|
||||
this->valmem_->copy(),
|
||||
this->len_->copy(), this->cap_->copy(),
|
||||
this->location());
|
||||
}
|
||||
|
||||
Bexpression*
|
||||
Slice_value_expression::do_get_backend(Translate_context* context)
|
||||
{
|
||||
std::vector<Bexpression*> vals(3);
|
||||
vals[0] = this->valptr_->get_backend(context);
|
||||
vals[0] = this->valmem_->get_backend(context);
|
||||
vals[1] = this->len_->get_backend(context);
|
||||
vals[2] = this->cap_->get_backend(context);
|
||||
|
||||
|
@ -15434,7 +15412,7 @@ Slice_value_expression::do_dump_expression(
|
|||
{
|
||||
ast_dump_context->ostream() << "slicevalue(";
|
||||
ast_dump_context->ostream() << "values: ";
|
||||
this->valptr_->dump_expression(ast_dump_context);
|
||||
this->valmem_->dump_expression(ast_dump_context);
|
||||
ast_dump_context->ostream() << ", length: ";
|
||||
this->len_->dump_expression(ast_dump_context);
|
||||
ast_dump_context->ostream() << ", capacity: ";
|
||||
|
@ -15443,11 +15421,14 @@ Slice_value_expression::do_dump_expression(
|
|||
}
|
||||
|
||||
Expression*
|
||||
Expression::make_slice_value(Type* at, Expression* valptr, Expression* len,
|
||||
Expression::make_slice_value(Type* at, Expression* valmem, Expression* len,
|
||||
Expression* cap, Location location)
|
||||
{
|
||||
go_assert(at->is_slice_type());
|
||||
return new Slice_value_expression(at, valptr, len, cap, location);
|
||||
go_assert(valmem->is_nil_expression()
|
||||
|| (at->array_type()->element_type()
|
||||
== valmem->type()->points_to()));
|
||||
return new Slice_value_expression(at, valmem, len, cap, location);
|
||||
}
|
||||
|
||||
// An expression that evaluates to some characteristic of a non-empty interface.
|
||||
|
|
|
@ -61,6 +61,7 @@ class Map_construction_expression;
|
|||
class Type_guard_expression;
|
||||
class Heap_expression;
|
||||
class Receive_expression;
|
||||
class Slice_value_expression;
|
||||
class Conditional_expression;
|
||||
class Compound_expression;
|
||||
class Numeric_constant;
|
||||
|
@ -841,6 +842,12 @@ class Expression
|
|||
receive_expression()
|
||||
{ return this->convert<Receive_expression, EXPRESSION_RECEIVE>(); }
|
||||
|
||||
// If this is a slice value expression, return the Slice_valiue_expression
|
||||
// structure. Otherwise, return NULL.
|
||||
Slice_value_expression*
|
||||
slice_value_expression()
|
||||
{ return this->convert<Slice_value_expression, EXPRESSION_SLICE_VALUE>(); }
|
||||
|
||||
// If this is a conditional expression, return the Conditional_expression
|
||||
// structure. Otherwise, return NULL.
|
||||
Conditional_expression*
|
||||
|
@ -3955,6 +3962,56 @@ class Receive_expression : public Expression
|
|||
Temporary_statement* temp_receiver_;
|
||||
};
|
||||
|
||||
// An expression that represents a slice value: a struct with value pointer,
|
||||
// length, and capacity fields.
|
||||
|
||||
class Slice_value_expression : public Expression
|
||||
{
|
||||
public:
|
||||
Slice_value_expression(Type* type, Expression* valmem, Expression* len,
|
||||
Expression* cap, Location location)
|
||||
: Expression(EXPRESSION_SLICE_VALUE, location),
|
||||
type_(type), valmem_(valmem), len_(len), cap_(cap)
|
||||
{ }
|
||||
|
||||
// The memory holding the values in the slice. The type should be a
|
||||
// pointer to the element value of the slice.
|
||||
Expression*
|
||||
valmem() const
|
||||
{ return this->valmem_; }
|
||||
|
||||
protected:
|
||||
int
|
||||
do_traverse(Traverse*);
|
||||
|
||||
Type*
|
||||
do_type()
|
||||
{ return this->type_; }
|
||||
|
||||
void
|
||||
do_determine_type(const Type_context*)
|
||||
{ }
|
||||
|
||||
Expression*
|
||||
do_copy();
|
||||
|
||||
Bexpression*
|
||||
do_get_backend(Translate_context* context);
|
||||
|
||||
void
|
||||
do_dump_expression(Ast_dump_context*) const;
|
||||
|
||||
private:
|
||||
// The type of the slice value.
|
||||
Type* type_;
|
||||
// The memory holding the values in the slice.
|
||||
Expression* valmem_;
|
||||
// The length of the slice.
|
||||
Expression* len_;
|
||||
// The capacity of the slice.
|
||||
Expression* cap_;
|
||||
};
|
||||
|
||||
// Conditional expressions.
|
||||
|
||||
class Conditional_expression : public Expression
|
||||
|
|
|
@ -85,10 +85,10 @@ DEF_GO_RUNTIME(COMPLEX128_DIV, "__go_complex128_div",
|
|||
|
||||
// Make a slice.
|
||||
DEF_GO_RUNTIME(MAKESLICE, "runtime.makeslice", P3(TYPE, INT, INT),
|
||||
R1(SLICE))
|
||||
R1(POINTER))
|
||||
|
||||
DEF_GO_RUNTIME(MAKESLICE64, "runtime.makeslice64", P3(TYPE, INT64, INT64),
|
||||
R1(SLICE))
|
||||
R1(POINTER))
|
||||
|
||||
|
||||
// Make a map with a hint and an (optional, unused) map structure.
|
||||
|
|
|
@ -41,6 +41,9 @@ class Mark_address_taken : public Traverse
|
|||
expression(Expression**);
|
||||
|
||||
private:
|
||||
Call_expression*
|
||||
find_makeslice_call(Expression*);
|
||||
|
||||
// General IR.
|
||||
Gogo* gogo_;
|
||||
// The function we are traversing.
|
||||
|
@ -97,6 +100,31 @@ Mark_address_taken::statement(Block* block, size_t* pindex, Statement* s)
|
|||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
// Look through the expression of a Slice_value_expression's valmem to
|
||||
// find an call to makeslice.
|
||||
|
||||
Call_expression*
|
||||
Mark_address_taken::find_makeslice_call(Expression* expr)
|
||||
{
|
||||
Unsafe_type_conversion_expression* utce =
|
||||
expr->unsafe_conversion_expression();
|
||||
if (utce != NULL)
|
||||
expr = utce->expr();
|
||||
|
||||
Call_expression* call = expr->call_expression();
|
||||
if (call == NULL)
|
||||
return NULL;
|
||||
|
||||
Func_expression* fe = call->fn()->func_expression();
|
||||
if (fe != NULL && fe->runtime_code() == Runtime::MAKESLICE)
|
||||
return call;
|
||||
|
||||
// We don't worry about MAKESLICE64 bcause we don't want to use a
|
||||
// stack allocation for a large slice anyhow.
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Mark variable addresses taken.
|
||||
|
||||
int
|
||||
|
@ -142,16 +170,12 @@ Mark_address_taken::expression(Expression** pexpr)
|
|||
}
|
||||
|
||||
// Rewrite non-escaping makeslice with constant size to stack allocation.
|
||||
Unsafe_type_conversion_expression* uce =
|
||||
expr->unsafe_conversion_expression();
|
||||
if (uce != NULL
|
||||
&& uce->type()->is_slice_type()
|
||||
&& Node::make_node(uce->expr())->encoding() == Node::ESCAPE_NONE
|
||||
&& uce->expr()->call_expression() != NULL)
|
||||
Slice_value_expression* sve = expr->slice_value_expression();
|
||||
if (sve != NULL)
|
||||
{
|
||||
Call_expression* call = uce->expr()->call_expression();
|
||||
if (call->fn()->func_expression() != NULL
|
||||
&& call->fn()->func_expression()->runtime_code() == Runtime::MAKESLICE)
|
||||
Call_expression* call = this->find_makeslice_call(sve->valmem());
|
||||
if (call != NULL
|
||||
&& Node::make_node(call)->encoding() == Node::ESCAPE_NONE)
|
||||
{
|
||||
Expression* len_arg = call->args()->at(1);
|
||||
Expression* cap_arg = call->args()->at(2);
|
||||
|
@ -164,8 +188,7 @@ Mark_address_taken::expression(Expression** pexpr)
|
|||
&& nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID
|
||||
&& nccap.to_unsigned_long(&vcap) == Numeric_constant::NC_UL_VALID)
|
||||
{
|
||||
// Turn it into a slice expression of an addressable array,
|
||||
// which is allocated on stack.
|
||||
// Stack allocate an array and make a slice value from it.
|
||||
Location loc = expr->location();
|
||||
Type* elmt_type = expr->type()->array_type()->element_type();
|
||||
Expression* len_expr =
|
||||
|
@ -173,10 +196,12 @@ Mark_address_taken::expression(Expression** pexpr)
|
|||
Type* array_type = Type::make_array_type(elmt_type, len_expr);
|
||||
Expression* alloc = Expression::make_allocation(array_type, loc);
|
||||
alloc->allocation_expression()->set_allocate_on_stack();
|
||||
Expression* array = Expression::make_unary(OPERATOR_MULT, alloc, loc);
|
||||
Expression* zero = Expression::make_integer_ul(0, len_arg->type(), loc);
|
||||
Expression* slice =
|
||||
Expression::make_array_index(array, zero, len_arg, cap_arg, loc);
|
||||
Type* ptr_type = Type::make_pointer_type(elmt_type);
|
||||
Expression* ptr = Expression::make_unsafe_cast(ptr_type, alloc,
|
||||
loc);
|
||||
Expression* slice =
|
||||
Expression::make_slice_value(expr->type(), ptr, len_arg,
|
||||
cap_arg, loc);
|
||||
*pexpr = slice;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func allocatePersistent1K() {
|
|||
// Allocate transient memory using reflect.Call.
|
||||
|
||||
func allocateReflectTransient() {
|
||||
memSink = make([]byte, 2<<20)
|
||||
memSink = make([]byte, 3<<20)
|
||||
}
|
||||
|
||||
func allocateReflect() {
|
||||
|
@ -106,7 +106,7 @@ func TestMemoryProfiler(t *testing.T) {
|
|||
// GC means that sometimes the value is not collected.
|
||||
fmt.Sprintf(`(0|%v): (0|%v) \[%v: %v\] @( 0x[0-9,a-f]+)+
|
||||
# 0x[0-9,a-f]+ pprof\.allocateReflectTransient\+0x[0-9,a-f]+ .*/mprof_test.go:48
|
||||
`, memoryProfilerRun, (2<<20)*memoryProfilerRun, memoryProfilerRun, (2<<20)*memoryProfilerRun),
|
||||
`, memoryProfilerRun, (3<<20)*memoryProfilerRun, memoryProfilerRun, (3<<20)*memoryProfilerRun),
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
|
|
@ -61,7 +61,7 @@ func panicmakeslicecap() {
|
|||
panic(errorString("makeslice: cap out of range"))
|
||||
}
|
||||
|
||||
func makeslice(et *_type, len, cap int) slice {
|
||||
func makeslice(et *_type, len, cap int) unsafe.Pointer {
|
||||
// NOTE: The len > maxElements check here is not strictly necessary,
|
||||
// but it produces a 'len out of range' error instead of a 'cap out of range' error
|
||||
// when someone does make([]T, bignumber). 'cap out of range' is true too,
|
||||
|
@ -76,11 +76,10 @@ func makeslice(et *_type, len, cap int) slice {
|
|||
panicmakeslicecap()
|
||||
}
|
||||
|
||||
p := mallocgc(et.size*uintptr(cap), et, true)
|
||||
return slice{p, len, cap}
|
||||
return mallocgc(et.size*uintptr(cap), et, true)
|
||||
}
|
||||
|
||||
func makeslice64(et *_type, len64, cap64 int64) slice {
|
||||
func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {
|
||||
len := int(len64)
|
||||
if int64(len) != len64 {
|
||||
panicmakeslicelen()
|
||||
|
|
Loading…
Add table
Reference in a new issue