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]).
Change-Id: I69739735e11b18abcd56a3108aee3ead8f7f2075
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/182540
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/expressions.cc b/go/expressions.cc
index 864b62d..a764d06 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -13082,11 +13082,6 @@
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 @@
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 @@
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);
- Btype* str_btype = strslice->type()->get_backend(gogo);
+ // 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);
+
Bexpression* index_error = bad_index->get_backend(context);
return gogo->backend()->conditional_expression(bfn, str_btype, index_error,
crash, bstrslice, loc);
diff --git a/go/expressions.h b/go/expressions.h
index 38dee04..2c505a9 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -3133,6 +3133,18 @@
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*);
diff --git a/go/runtime.def b/go/runtime.def
index c81ab79..ffc747b 100644
--- a/go/runtime.def
+++ b/go/runtime.def
@@ -45,10 +45,6 @@
// 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))
diff --git a/go/statements.cc b/go/statements.cc
index 7f424fd..e8380be 100644
--- a/go/statements.cc
+++ b/go/statements.cc
@@ -1021,6 +1021,18 @@
&& 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;
}
diff --git a/libgo/Makefile.am b/libgo/Makefile.am
index 6324170..4bfed3f 100644
--- a/libgo/Makefile.am
+++ b/libgo/Makefile.am
@@ -468,7 +468,6 @@
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 \
diff --git a/libgo/Makefile.in b/libgo/Makefile.in
index 08a3926..837e1e8 100644
--- a/libgo/Makefile.in
+++ b/libgo/Makefile.in
@@ -248,12 +248,12 @@
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/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/$(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 @@
@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@
diff --git a/libgo/runtime/go-strslice.c b/libgo/runtime/go-strslice.c
deleted file mode 100644
index d51c249..0000000
--- a/libgo/runtime/go-strslice.c
+++ /dev/null
@@ -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;
-}