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 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.
...@@ -3739,8 +3739,11 @@ Type_conversion_expression::do_flatten(Gogo*, Named_object*, ...@@ -3739,8 +3739,11 @@ Type_conversion_expression::do_flatten(Gogo*, Named_object*,
this->expr_ = Expression::make_temporary_reference(temp, this->location()); this->expr_ = Expression::make_temporary_reference(temp, this->location());
} }
// For interface conversion, decide if we can allocate on stack. // For interface conversion and string to/from slice conversions,
if (this->type()->interface_type() != NULL) // 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); Node* n = Node::make_node(this);
if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE) if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
...@@ -3984,9 +3987,21 @@ Type_conversion_expression::do_get_backend(Translate_context* context) ...@@ -3984,9 +3987,21 @@ Type_conversion_expression::do_get_backend(Translate_context* context)
return se->get_backend(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 = Expression* i2s_expr =
Runtime::make_call(Runtime::INTSTRING, loc, 2, Runtime::make_call(Runtime::INTSTRING, loc, 2, buf, this->expr_);
Expression::make_nil(loc), this->expr_);
return Expression::make_cast(type, i2s_expr, loc)->get_backend(context); return Expression::make_cast(type, i2s_expr, loc)->get_backend(context);
} }
else if (type->is_string_type() && expr_type->is_slice_type()) else if (type->is_string_type() && expr_type->is_slice_type())
...@@ -4019,7 +4034,21 @@ Type_conversion_expression::do_get_backend(Translate_context* context) ...@@ -4019,7 +4034,21 @@ Type_conversion_expression::do_get_backend(Translate_context* context)
go_assert(e->integer_type()->is_rune()); go_assert(e->integer_type()->is_rune());
code = Runtime::SLICERUNETOSTRING; 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); this->expr_)->get_backend(context);
} }
else if (type->is_slice_type() && expr_type->is_string_type()) else if (type->is_slice_type() && expr_type->is_string_type())
...@@ -4035,9 +4064,20 @@ Type_conversion_expression::do_get_backend(Translate_context* context) ...@@ -4035,9 +4064,20 @@ Type_conversion_expression::do_get_backend(Translate_context* context)
go_assert(e->integer_type()->is_rune()); go_assert(e->integer_type()->is_rune());
code = Runtime::STRINGTOSLICERUNE; code = Runtime::STRINGTOSLICERUNE;
} }
Expression* s2a = Runtime::make_call(code, loc, 2,
Expression::make_nil(loc), Expression* buf;
this->expr_); 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); return Expression::make_unsafe_cast(type, s2a, loc)->get_backend(context);
} }
else if (type->is_numeric_type()) else if (type->is_numeric_type())
...@@ -7428,7 +7468,35 @@ String_concat_expression::do_flatten(Gogo*, Named_object*, ...@@ -7428,7 +7468,35 @@ String_concat_expression::do_flatten(Gogo*, Named_object*,
tce->set_no_copy(true); 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; Expression* call;
switch (this->exprs_->size()) switch (this->exprs_->size())
{ {
...@@ -7462,7 +7530,7 @@ String_concat_expression::do_flatten(Gogo*, Named_object*, ...@@ -7462,7 +7530,7 @@ String_concat_expression::do_flatten(Gogo*, Named_object*,
code = Runtime::CONCATSTRING5; code = Runtime::CONCATSTRING5;
break; break;
} }
call = Runtime::make_call(code, loc, 2, nil_arg, arg); call = Runtime::make_call(code, loc, 2, buf, arg);
} }
break; break;
...@@ -7473,7 +7541,7 @@ String_concat_expression::do_flatten(Gogo*, Named_object*, ...@@ -7473,7 +7541,7 @@ String_concat_expression::do_flatten(Gogo*, Named_object*,
Expression::make_slice_composite_literal(arg_type, this->exprs_, Expression::make_slice_composite_literal(arg_type, this->exprs_,
loc); loc);
sce->set_storage_does_not_escape(); 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); sce);
} }
break; break;
...@@ -14254,6 +14322,8 @@ Allocation_expression::do_copy() ...@@ -14254,6 +14322,8 @@ Allocation_expression::do_copy()
this->location()); this->location());
if (this->allocate_on_stack_) if (this->allocate_on_stack_)
alloc->set_allocate_on_stack(); alloc->set_allocate_on_stack();
if (this->no_zero_)
alloc->set_no_zero();
return alloc; return alloc;
} }
...@@ -14279,10 +14349,12 @@ Allocation_expression::do_get_backend(Translate_context* context) ...@@ -14279,10 +14349,12 @@ Allocation_expression::do_get_backend(Translate_context* context)
Named_object* fn = context->function(); Named_object* fn = context->function();
go_assert(fn != NULL); go_assert(fn != NULL);
Bfunction* fndecl = fn->func_value()->get_or_make_decl(gogo, fn); 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 = Bvariable* temp =
gogo->backend()->temporary_variable(fndecl, context->bblock(), btype, gogo->backend()->temporary_variable(fndecl, context->bblock(), btype,
zero, true, loc, &decl); init, true, loc, &decl);
Bexpression* ret = gogo->backend()->var_expression(temp, loc); Bexpression* ret = gogo->backend()->var_expression(temp, loc);
ret = gogo->backend()->address_expression(ret, loc); ret = gogo->backend()->address_expression(ret, loc);
ret = gogo->backend()->compound_expression(decl, ret, loc); ret = gogo->backend()->compound_expression(decl, ret, loc);
......
...@@ -1822,9 +1822,8 @@ class Type_conversion_expression : public Expression ...@@ -1822,9 +1822,8 @@ class Type_conversion_expression : public Expression
// True if a string([]byte) conversion can reuse the backing store // True if a string([]byte) conversion can reuse the backing store
// without copying. Only used in string([]byte) conversion. // without copying. Only used in string([]byte) conversion.
bool no_copy_; bool no_copy_;
// True if a conversion to interface does not escape, so it does // True if a conversion does not escape. Used in type-to-interface
// not need a heap allocation. Only used in type-to-interface // conversions and slice-to/from-string conversions.
// conversion.
bool no_escape_; bool no_escape_;
}; };
...@@ -3561,13 +3560,19 @@ class Allocation_expression : public Expression ...@@ -3561,13 +3560,19 @@ class Allocation_expression : public Expression
public: public:
Allocation_expression(Type* type, Location location) Allocation_expression(Type* type, Location location)
: Expression(EXPRESSION_ALLOCATION, location), : Expression(EXPRESSION_ALLOCATION, location),
type_(type), allocate_on_stack_(false) type_(type), allocate_on_stack_(false),
no_zero_(false)
{ } { }
void void
set_allocate_on_stack() set_allocate_on_stack()
{ this->allocate_on_stack_ = true; } { this->allocate_on_stack_ = true; }
// Mark that the allocated memory doesn't need zeroing.
void
set_no_zero()
{ this->no_zero_ = true; }
protected: protected:
int int
do_traverse(Traverse*); do_traverse(Traverse*);
...@@ -3596,6 +3601,8 @@ class Allocation_expression : public Expression ...@@ -3596,6 +3601,8 @@ class Allocation_expression : public Expression
Type* type_; Type* type_;
// Whether or not this is a stack allocation. // Whether or not this is a stack allocation.
bool allocate_on_stack_; 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 // A general composite literal. This is lowered to a type specific
...@@ -4541,4 +4548,8 @@ class Numeric_constant ...@@ -4541,4 +4548,8 @@ class Numeric_constant
Type* type_; 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) #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