compiler: open code string equality

Open code string equality with builtin memcmp. This allows
    further optimizations in the backend.
    
    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/183538

From-SVN: r272624
This commit is contained in:
Ian Lance Taylor 2019-06-24 17:54:07 +00:00
parent d611cec3de
commit 609c7da9ab
5 changed files with 52 additions and 28 deletions

View File

@ -1,4 +1,4 @@
338e4baf88a4ae676205dff601dbef2d31b19d2d 89b442a0100286ee569b8d2562ce1b2ea602f7e7
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.

View File

@ -6226,10 +6226,27 @@ Binary_expression::do_flatten(Gogo* gogo, Named_object*,
bool is_idiv_op = ((this->op_ == OPERATOR_DIV && bool is_idiv_op = ((this->op_ == OPERATOR_DIV &&
left_type->integer_type() != NULL) left_type->integer_type() != NULL)
|| this->op_ == OPERATOR_MOD); || this->op_ == OPERATOR_MOD);
bool is_string_op = (left_type->is_string_type()
&& this->right_->type()->is_string_type());
if (is_string_op)
{
// Mark string([]byte) operands to reuse the backing store.
// String comparison does not keep the reference, so it is safe.
Type_conversion_expression* lce =
this->left_->conversion_expression();
if (lce != NULL && lce->expr()->type()->is_slice_type())
lce->set_no_copy(true);
Type_conversion_expression* rce =
this->right_->conversion_expression();
if (rce != NULL && rce->expr()->type()->is_slice_type())
rce->set_no_copy(true);
}
if (is_shift_op if (is_shift_op
|| (is_idiv_op || (is_idiv_op
&& (gogo->check_divide_by_zero() || gogo->check_divide_overflow()))) && (gogo->check_divide_by_zero() || gogo->check_divide_overflow()))
|| is_string_op)
{ {
if (!this->left_->is_variable() && !this->left_->is_constant()) if (!this->left_->is_variable() && !this->left_->is_constant())
{ {
@ -7217,19 +7234,42 @@ Expression::comparison(Translate_context* context, Type* result_type,
if (left_type->is_string_type() && right_type->is_string_type()) if (left_type->is_string_type() && right_type->is_string_type())
{ {
// Mark string([]byte) operands to reuse the backing store. go_assert(left->is_variable() || left->is_constant());
// String comparison does not keep the reference, so it is safe. go_assert(right->is_variable() || right->is_constant());
Type_conversion_expression* lce = left->conversion_expression();
if (lce != NULL && lce->expr()->type()->is_slice_type())
lce->set_no_copy(true);
Type_conversion_expression* rce = right->conversion_expression();
if (rce != NULL && rce->expr()->type()->is_slice_type())
rce->set_no_copy(true);
if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ) if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ)
{ {
left = Runtime::make_call(Runtime::EQSTRING, location, 2, // (l.len == r.len
left, right); // ? (l.ptr == r.ptr ? true : memcmp(l.ptr, r.ptr, r.len) == 0)
// : false)
Expression* llen = Expression::make_string_info(left,
STRING_INFO_LENGTH,
location);
Expression* rlen = Expression::make_string_info(right,
STRING_INFO_LENGTH,
location);
Expression* leneq = Expression::make_binary(OPERATOR_EQEQ, llen, rlen,
location);
Expression* lptr = Expression::make_string_info(left->copy(),
STRING_INFO_DATA,
location);
Expression* rptr = Expression::make_string_info(right->copy(),
STRING_INFO_DATA,
location);
Expression* ptreq = Expression::make_binary(OPERATOR_EQEQ, lptr, rptr,
location);
Expression* btrue = Expression::make_boolean(true, location);
Expression* call = Runtime::make_call(Runtime::MEMCMP, location, 3,
lptr->copy(), rptr->copy(),
rlen->copy());
Type* int32_type = Type::lookup_integer_type("int32");
Expression* zero = Expression::make_integer_ul(0, int32_type, location);
Expression* cmp = Expression::make_binary(OPERATOR_EQEQ, call, zero,
location);
Expression* cond = Expression::make_conditional(ptreq, btrue, cmp,
location);
Expression* bfalse = Expression::make_boolean(false, location);
left = Expression::make_conditional(leneq, cond, bfalse, location);
right = Expression::make_boolean(true, location); right = Expression::make_boolean(true, location);
} }
else else

View File

@ -39,9 +39,6 @@ DEF_GO_RUNTIME(DECODERUNE, "runtime.decoderune", P2(STRING, INT),
DEF_GO_RUNTIME(CONCATSTRINGS, "runtime.concatstrings", DEF_GO_RUNTIME(CONCATSTRINGS, "runtime.concatstrings",
P3(POINTER, POINTER, INT), R1(STRING)) P3(POINTER, POINTER, INT), R1(STRING))
// Compare two strings for equality.
DEF_GO_RUNTIME(EQSTRING, "runtime.eqstring", P2(STRING, STRING), R1(BOOL))
// Compare two strings. // Compare two strings.
DEF_GO_RUNTIME(CMPSTRING, "runtime.cmpstring", P2(STRING, STRING), R1(INT)) DEF_GO_RUNTIME(CMPSTRING, "runtime.cmpstring", P2(STRING, STRING), R1(INT))

View File

@ -44,7 +44,6 @@ import (
//go:linkname ifacevaleq runtime.ifacevaleq //go:linkname ifacevaleq runtime.ifacevaleq
//go:linkname ifaceefaceeq runtime.ifaceefaceeq //go:linkname ifaceefaceeq runtime.ifaceefaceeq
//go:linkname efacevaleq runtime.efacevaleq //go:linkname efacevaleq runtime.efacevaleq
//go:linkname eqstring runtime.eqstring
//go:linkname cmpstring runtime.cmpstring //go:linkname cmpstring runtime.cmpstring
// //
// Temporary to be called from C code. // Temporary to be called from C code.

View File

@ -273,18 +273,6 @@ func checkASM() bool {
return true return true
} }
func eqstring(x, y string) bool {
a := stringStructOf(&x)
b := stringStructOf(&y)
if a.len != b.len {
return false
}
if a.str == b.str {
return true
}
return memequal(a.str, b.str, uintptr(a.len))
}
// For gccgo this is in the C code. // For gccgo this is in the C code.
func osyield() func osyield()