Commit 20b603db by Ian Lance Taylor

compiler: stack allocate a buffer for non-escaping string ops

    
    For string concatenation, string to/from byte or rune slice
    conversion, and int to string conversion, if the result does not
    escape, we can allocate a small (32-element, or 4-byte for int to
    string) buffer on stack, and pass it to the runtime function. If
    the result fits in the buffer, it doesn't need to do a heap
    allocation.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/182538

From-SVN: r272468
parent 17f62b7e
62d1b667f3e85f72a186b04aad36d701160a4611
0e4aa31b26a20b6a6a2ca102b85ba8c8b8cdf876
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
......@@ -3739,8 +3739,11 @@ Type_conversion_expression::do_flatten(Gogo*, Named_object*,
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)
// For interface conversion and string to/from slice conversions,
// decide if we can allocate on stack.
if (this->type()->interface_type() != NULL
|| this->type()->is_string_type()
|| this->expr_->type()->is_string_type())
{
Node* n = Node::make_node(this);
if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
......@@ -3984,9 +3987,21 @@ Type_conversion_expression::do_get_backend(Translate_context* context)
return se->get_backend(context);
}
Expression* buf;
if (this->no_escape_)
{
Type* byte_type = Type::lookup_integer_type("uint8");
Expression* buflen =
Expression::make_integer_ul(4, NULL, loc);
Type* array_type = Type::make_array_type(byte_type, buflen);
buf = Expression::make_allocation(array_type, loc);
buf->allocation_expression()->set_allocate_on_stack();
buf->allocation_expression()->set_no_zero();
}
else
buf = Expression::make_nil(loc);
Expression* i2s_expr =
Runtime::make_call(Runtime::INTSTRING, loc, 2,
Expression::make_nil(loc), this->expr_);
Runtime::make_call(Runtime::INTSTRING, loc, 2, buf, this->expr_);
return Expression::make_cast(type, i2s_expr, loc)->get_backend(context);
}
else if (type->is_string_type() && expr_type->is_slice_type())
......@@ -4019,7 +4034,21 @@ Type_conversion_expression::do_get_backend(Translate_context* context)
go_assert(e->integer_type()->is_rune());
code = Runtime::SLICERUNETOSTRING;
}
return Runtime::make_call(code, loc, 2, Expression::make_nil(loc),
Expression* buf;
if (this->no_escape_)
{
Type* byte_type = Type::lookup_integer_type("uint8");
Expression* buflen =
Expression::make_integer_ul(tmp_string_buf_size, NULL, loc);
Type* array_type = Type::make_array_type(byte_type, buflen);
buf = Expression::make_allocation(array_type, loc);
buf->allocation_expression()->set_allocate_on_stack();
buf->allocation_expression()->set_no_zero();
}
else
buf = Expression::make_nil(loc);
return Runtime::make_call(code, loc, 2, buf,
this->expr_)->get_backend(context);
}
else if (type->is_slice_type() && expr_type->is_string_type())
......@@ -4035,9 +4064,20 @@ Type_conversion_expression::do_get_backend(Translate_context* context)
go_assert(e->integer_type()->is_rune());
code = Runtime::STRINGTOSLICERUNE;
}
Expression* s2a = Runtime::make_call(code, loc, 2,
Expression::make_nil(loc),
this->expr_);
Expression* buf;
if (this->no_escape_)
{
Expression* buflen =
Expression::make_integer_ul(tmp_string_buf_size, NULL, loc);
Type* array_type = Type::make_array_type(e, buflen);
buf = Expression::make_allocation(array_type, loc);
buf->allocation_expression()->set_allocate_on_stack();
buf->allocation_expression()->set_no_zero();
}
else
buf = Expression::make_nil(loc);
Expression* s2a = Runtime::make_call(code, loc, 2, buf, this->expr_);
return Expression::make_unsafe_cast(type, s2a, loc)->get_backend(context);
}
else if (type->is_numeric_type())
......@@ -7428,7 +7468,35 @@ String_concat_expression::do_flatten(Gogo*, Named_object*,
tce->set_no_copy(true);
}
Expression* nil_arg = Expression::make_nil(loc);
Expression* buf = NULL;
Node* n = Node::make_node(this);
if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
{
size_t size = 0;
for (Expression_list::iterator p = this->exprs_->begin();
p != this->exprs_->end();
++p)
{
std::string s;
if ((*p)->string_constant_value(&s))
size += s.length();
}
// Make a buffer on stack if the result does not escape.
// But don't do this if we know it won't fit.
if (size < (size_t)tmp_string_buf_size)
{
Type* byte_type = Type::lookup_integer_type("uint8");
Expression* buflen =
Expression::make_integer_ul(tmp_string_buf_size, NULL, loc);
Expression::make_integer_ul(tmp_string_buf_size, NULL, loc);
Type* array_type = Type::make_array_type(byte_type, buflen);
buf = Expression::make_allocation(array_type, loc);
buf->allocation_expression()->set_allocate_on_stack();
buf->allocation_expression()->set_no_zero();
}
}
if (buf == NULL)
buf = Expression::make_nil(loc);
Expression* call;
switch (this->exprs_->size())
{
......@@ -7462,7 +7530,7 @@ String_concat_expression::do_flatten(Gogo*, Named_object*,
code = Runtime::CONCATSTRING5;
break;
}
call = Runtime::make_call(code, loc, 2, nil_arg, arg);
call = Runtime::make_call(code, loc, 2, buf, arg);
}
break;
......@@ -7473,7 +7541,7 @@ String_concat_expression::do_flatten(Gogo*, Named_object*,
Expression::make_slice_composite_literal(arg_type, this->exprs_,
loc);
sce->set_storage_does_not_escape();
call = Runtime::make_call(Runtime::CONCATSTRINGS, loc, 2, nil_arg,
call = Runtime::make_call(Runtime::CONCATSTRINGS, loc, 2, buf,
sce);
}
break;
......@@ -14254,6 +14322,8 @@ Allocation_expression::do_copy()
this->location());
if (this->allocate_on_stack_)
alloc->set_allocate_on_stack();
if (this->no_zero_)
alloc->set_no_zero();
return alloc;
}
......@@ -14279,10 +14349,12 @@ Allocation_expression::do_get_backend(Translate_context* context)
Named_object* fn = context->function();
go_assert(fn != NULL);
Bfunction* fndecl = fn->func_value()->get_or_make_decl(gogo, fn);
Bexpression* zero = gogo->backend()->zero_expression(btype);
Bexpression* init = (this->no_zero_
? NULL
: gogo->backend()->zero_expression(btype));
Bvariable* temp =
gogo->backend()->temporary_variable(fndecl, context->bblock(), btype,
zero, true, loc, &decl);
init, true, loc, &decl);
Bexpression* ret = gogo->backend()->var_expression(temp, loc);
ret = gogo->backend()->address_expression(ret, loc);
ret = gogo->backend()->compound_expression(decl, ret, loc);
......
......@@ -1822,9 +1822,8 @@ 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.
// True if a conversion does not escape. Used in type-to-interface
// conversions and slice-to/from-string conversions.
bool no_escape_;
};
......@@ -3561,13 +3560,19 @@ class Allocation_expression : public Expression
public:
Allocation_expression(Type* type, Location location)
: Expression(EXPRESSION_ALLOCATION, location),
type_(type), allocate_on_stack_(false)
type_(type), allocate_on_stack_(false),
no_zero_(false)
{ }
void
set_allocate_on_stack()
{ this->allocate_on_stack_ = true; }
// Mark that the allocated memory doesn't need zeroing.
void
set_no_zero()
{ this->no_zero_ = true; }
protected:
int
do_traverse(Traverse*);
......@@ -3596,6 +3601,8 @@ class Allocation_expression : public Expression
Type* type_;
// Whether or not this is a stack allocation.
bool allocate_on_stack_;
// Whether we don't need to zero the allocated memory.
bool no_zero_;
};
// A general composite literal. This is lowered to a type specific
......@@ -4541,4 +4548,8 @@ class Numeric_constant
Type* type_;
};
// Temporary buffer size for string conversions.
// Also known to the runtime as tmpStringBufSize in runtime/string.go.
static const int tmp_string_buf_size = 32;
#endif // !defined(GO_EXPRESSIONS_H)
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