compiler: open code string slice expressions

Currently a string slice expression is implemented with a runtime
    call __go_string_slice. Change it to open code it, which is more
    efficient, and allows the backend to further optimize it.
    
    Also omit the write barrier for length-only update (i.e.
    s = s[:n]).
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/182540

From-SVN: r272549
This commit is contained in:
Ian Lance Taylor 2019-06-21 14:14:58 +00:00
parent 050e182a75
commit c9b236e5ca
8 changed files with 80 additions and 63 deletions

View file

@ -1,4 +1,4 @@
7822080a6e226b1e5872e2fcefac30f666f4cc1e
62e3a8cc0a862b0abd3d0b1ef6cf4b228992a137
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View file

@ -13082,11 +13082,6 @@ Bexpression*
String_index_expression::do_get_backend(Translate_context* context)
{
Location loc = this->location();
Expression* string_arg = this->string_;
if (this->string_->type()->points_to() != NULL)
string_arg = Expression::make_dereference(this->string_,
NIL_CHECK_NOT_NEEDED, loc);
Expression* bad_index = Expression::check_bounds(this->start_, loc);
int code = (this->end_ == NULL
@ -13110,23 +13105,27 @@ String_index_expression::do_get_backend(Translate_context* context)
return context->backend()->error_expression();
}
go_assert(this->string_->is_variable());
go_assert(this->start_->is_variable());
Expression* start = Expression::make_cast(int_type, this->start_, loc);
Bfunction* bfn = context->function()->func_value()->get_decl();
Expression* length =
Expression::make_string_info(this->string_, STRING_INFO_LENGTH, loc);
Expression* bytes =
Expression::make_string_info(this->string_, STRING_INFO_DATA, loc);
Bexpression* bstart = start->get_backend(context);
Bexpression* ptr = bytes->get_backend(context);
if (this->end_ == NULL)
{
Expression* length =
Expression::make_string_info(this->string_, STRING_INFO_LENGTH, loc);
Expression* start_too_large =
Expression::make_binary(OPERATOR_GE, start, length, loc);
bad_index = Expression::make_binary(OPERATOR_OROR, start_too_large,
bad_index, loc);
Expression* bytes =
Expression::make_string_info(this->string_, STRING_INFO_DATA, loc);
Bexpression* bstart = start->get_backend(context);
Bexpression* ptr = bytes->get_backend(context);
ptr = gogo->backend()->pointer_offset_expression(ptr, bstart, loc);
Btype* ubtype = Type::lookup_integer_type("uint8")->get_backend(gogo);
Bexpression* index =
@ -13141,20 +13140,53 @@ String_index_expression::do_get_backend(Translate_context* context)
Expression* end = NULL;
if (this->end_->is_nil_expression())
end = Expression::make_integer_sl(-1, int_type, loc);
end = length;
else
{
go_assert(this->end_->is_variable());
Expression* bounds_check = Expression::check_bounds(this->end_, loc);
bad_index =
Expression::make_binary(OPERATOR_OROR, bounds_check, bad_index, loc);
end = Expression::make_cast(int_type, this->end_, loc);
Expression* end_too_large =
Expression::make_binary(OPERATOR_GT, end, length, loc);
bad_index = Expression::make_binary(OPERATOR_OROR, end_too_large,
bad_index, loc);
}
Expression* start_too_large =
Expression::make_binary(OPERATOR_GT, start->copy(), end->copy(), loc);
bad_index = Expression::make_binary(OPERATOR_OROR, start_too_large,
bad_index, loc);
Expression* strslice = Runtime::make_call(Runtime::STRING_SLICE, loc, 3,
string_arg, start, end);
Bexpression* bstrslice = strslice->get_backend(context);
end = end->copy();
Bexpression* bend = end->get_backend(context);
Bexpression* new_length =
gogo->backend()->binary_expression(OPERATOR_MINUS, bend, bstart, loc);
// If the new length is zero, don't change pointer. Otherwise we can
// get a pointer to the next object in memory, keeping it live
// unnecessarily. When the length is zero, the actual pointer
// value doesn't matter.
Btype* int_btype = int_type->get_backend(gogo);
Bexpression* zero =
Expression::make_integer_ul(0, int_type, loc)->get_backend(context);
Bexpression* cond =
gogo->backend()->binary_expression(OPERATOR_EQEQ, new_length, zero,
loc);
Bexpression* offset =
gogo->backend()->conditional_expression(bfn, int_btype, cond, zero,
bstart, loc);
ptr = gogo->backend()->pointer_offset_expression(ptr, offset, loc);
Btype* str_btype = this->type()->get_backend(gogo);
std::vector<Bexpression*> init;
init.push_back(ptr);
init.push_back(new_length);
Bexpression* bstrslice =
gogo->backend()->constructor_expression(str_btype, init, loc);
Btype* str_btype = strslice->type()->get_backend(gogo);
Bexpression* index_error = bad_index->get_backend(context);
return gogo->backend()->conditional_expression(bfn, str_btype, index_error,
crash, bstrslice, loc);

View file

@ -3133,6 +3133,18 @@ class String_index_expression : public Expression
string() const
{ return this->string_; }
// Return the index of a simple index expression, or the start index
// of a slice expression.
Expression*
start() const
{ return this->start_; }
// Return the end index of a slice expression. This is NULL for a
// simple index expression.
Expression*
end() const
{ return this->end_; }
protected:
int
do_traverse(Traverse*);

View file

@ -45,10 +45,6 @@ DEF_GO_RUNTIME(EQSTRING, "runtime.eqstring", P2(STRING, STRING), R1(BOOL))
// Compare two strings.
DEF_GO_RUNTIME(CMPSTRING, "runtime.cmpstring", P2(STRING, STRING), R1(INT))
// Take a slice of a string.
DEF_GO_RUNTIME(STRING_SLICE, "__go_string_slice", P3(STRING, INT, INT),
R1(STRING))
// Convert an integer to a string.
DEF_GO_RUNTIME(INTSTRING, "runtime.intstring", P2(POINTER, INT64), R1(STRING))

View file

@ -1021,6 +1021,18 @@ Assignment_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
&& ival == 0)
this->omit_write_barrier_ = true;
}
String_index_expression* sie = this->rhs_->string_index_expression();
if (sie != NULL
&& sie->end() != NULL
&& Expression::is_same_variable(this->lhs_, sie->string()))
{
Numeric_constant nc;
unsigned long ival;
if (sie->start()->numeric_constant_value(&nc)
&& nc.to_unsigned_long(&ival) == Numeric_constant::NC_UL_VALID
&& ival == 0)
this->omit_write_barrier_ = true;
}
return this;
}

View file

@ -468,7 +468,6 @@ runtime_files = \
runtime/go-runtime-error.c \
runtime/go-setenv.c \
runtime/go-signal.c \
runtime/go-strslice.c \
runtime/go-unsafe-pointer.c \
runtime/go-unsetenv.c \
runtime/go-unwind.c \

View file

@ -248,12 +248,12 @@ am__objects_3 = runtime/aeshash.lo runtime/go-assert.lo \
runtime/go-nanotime.lo runtime/go-now.lo runtime/go-nosys.lo \
runtime/go-reflect-call.lo runtime/go-runtime-error.lo \
runtime/go-setenv.lo runtime/go-signal.lo \
runtime/go-strslice.lo runtime/go-unsafe-pointer.lo \
runtime/go-unsetenv.lo runtime/go-unwind.lo \
runtime/go-varargs.lo runtime/env_posix.lo runtime/panic.lo \
runtime/print.lo runtime/proc.lo runtime/runtime_c.lo \
runtime/stack.lo runtime/yield.lo runtime/go-context.lo \
$(am__objects_1) $(am__objects_2)
runtime/go-unsafe-pointer.lo runtime/go-unsetenv.lo \
runtime/go-unwind.lo runtime/go-varargs.lo \
runtime/env_posix.lo runtime/panic.lo runtime/print.lo \
runtime/proc.lo runtime/runtime_c.lo runtime/stack.lo \
runtime/yield.lo runtime/go-context.lo $(am__objects_1) \
$(am__objects_2)
am_libgo_llgo_la_OBJECTS = $(am__objects_3)
libgo_llgo_la_OBJECTS = $(am_libgo_llgo_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
@ -901,7 +901,6 @@ runtime_files = \
runtime/go-runtime-error.c \
runtime/go-setenv.c \
runtime/go-signal.c \
runtime/go-strslice.c \
runtime/go-unsafe-pointer.c \
runtime/go-unsetenv.c \
runtime/go-unwind.c \
@ -1362,8 +1361,6 @@ runtime/go-setenv.lo: runtime/$(am__dirstamp) \
runtime/$(DEPDIR)/$(am__dirstamp)
runtime/go-signal.lo: runtime/$(am__dirstamp) \
runtime/$(DEPDIR)/$(am__dirstamp)
runtime/go-strslice.lo: runtime/$(am__dirstamp) \
runtime/$(DEPDIR)/$(am__dirstamp)
runtime/go-unsafe-pointer.lo: runtime/$(am__dirstamp) \
runtime/$(DEPDIR)/$(am__dirstamp)
runtime/go-unsetenv.lo: runtime/$(am__dirstamp) \
@ -1448,7 +1445,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-runtime-error.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-setenv.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-signal.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-strslice.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-unsafe-pointer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-unsetenv.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@runtime/$(DEPDIR)/go-unwind.Plo@am__quote@

View file

@ -1,30 +0,0 @@
/* go-strslice.c -- the go string slice function.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include "runtime.h"
String
__go_string_slice (String s, intgo start, intgo end)
{
intgo len;
String ret;
len = s.len;
if (end == -1)
end = len;
if (start > len || end < start || end > len)
runtime_panicstring ("string index out of bounds");
ret.len = end - start;
// If the length of the new string is zero, the str field doesn't
// matter, so just set it to nil. This avoids the problem of
// s.str + start pointing just past the end of the string,
// which may keep the next memory block alive unnecessarily.
if (ret.len == 0)
ret.str = nil;
else
ret.str = s.str + start;
return ret;
}