From c9b236e5cafaea9d09ff8102140c72eb3d70e302 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 21 Jun 2019 14:14:58 +0000 Subject: [PATCH] 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 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 66 ++++++++++++++++++++++++-------- gcc/go/gofrontend/expressions.h | 12 ++++++ gcc/go/gofrontend/runtime.def | 4 -- gcc/go/gofrontend/statements.cc | 12 ++++++ libgo/Makefile.am | 1 - libgo/Makefile.in | 16 +++----- libgo/runtime/go-strslice.c | 30 --------------- 8 files changed, 80 insertions(+), 63 deletions(-) delete mode 100644 libgo/runtime/go-strslice.c diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 3b0cff742eb..10104a78197 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -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. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 864b62df3b1..a764d06509c 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -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); + + end = end->copy(); + Bexpression* bend = end->get_backend(context); + Bexpression* new_length = + gogo->backend()->binary_expression(OPERATOR_MINUS, bend, bstart, loc); - Expression* strslice = Runtime::make_call(Runtime::STRING_SLICE, loc, 3, - string_arg, start, end); - Bexpression* bstrslice = strslice->get_backend(context); + // 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 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); diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 38dee04ca97..2c505a9743f 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -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*); diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index c81ab79cc0f..ffc747bb575 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -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)) diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 7f424fd7b7a..e8380be0bb0 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -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; } diff --git a/libgo/Makefile.am b/libgo/Makefile.am index 6324170816a..4bfed3facf7 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -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 \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 08a39266ccc..837e1e8a621 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -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@ diff --git a/libgo/runtime/go-strslice.c b/libgo/runtime/go-strslice.c deleted file mode 100644 index d51c2493781..00000000000 --- 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; -} -- 2.30.2