From 609c7da9ab08b009fb7a9cacf68a40b3b39231b3 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 24 Jun 2019 17:54:07 +0000 Subject: [PATCH] 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 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 62 ++++++++++++++++++++++++++------ gcc/go/gofrontend/runtime.def | 3 -- libgo/go/runtime/alg.go | 1 - libgo/go/runtime/stubs.go | 12 ------- 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 89d401b6a3ed..ca68507906a9 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -338e4baf88a4ae676205dff601dbef2d31b19d2d +89b442a0100286ee569b8d2562ce1b2ea602f7e7 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 050da32c02f4..2f33deeb4a1b 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -6226,10 +6226,27 @@ Binary_expression::do_flatten(Gogo* gogo, Named_object*, bool is_idiv_op = ((this->op_ == OPERATOR_DIV && left_type->integer_type() != NULL) || 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 || (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()) { @@ -7217,19 +7234,42 @@ Expression::comparison(Translate_context* context, Type* result_type, if (left_type->is_string_type() && right_type->is_string_type()) { - // Mark string([]byte) operands to reuse the backing store. - // String comparison does not keep the reference, so it is safe. - 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); + go_assert(left->is_variable() || left->is_constant()); + go_assert(right->is_variable() || right->is_constant()); if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ) { - left = Runtime::make_call(Runtime::EQSTRING, location, 2, - left, right); + // (l.len == r.len + // ? (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); } else diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 7b66b166b770..ec7ec769048d 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -39,9 +39,6 @@ DEF_GO_RUNTIME(DECODERUNE, "runtime.decoderune", P2(STRING, INT), DEF_GO_RUNTIME(CONCATSTRINGS, "runtime.concatstrings", 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. DEF_GO_RUNTIME(CMPSTRING, "runtime.cmpstring", P2(STRING, STRING), R1(INT)) diff --git a/libgo/go/runtime/alg.go b/libgo/go/runtime/alg.go index ec951e38cf51..a2bb5bb0b998 100644 --- a/libgo/go/runtime/alg.go +++ b/libgo/go/runtime/alg.go @@ -44,7 +44,6 @@ import ( //go:linkname ifacevaleq runtime.ifacevaleq //go:linkname ifaceefaceeq runtime.ifaceefaceeq //go:linkname efacevaleq runtime.efacevaleq -//go:linkname eqstring runtime.eqstring //go:linkname cmpstring runtime.cmpstring // // Temporary to be called from C code. diff --git a/libgo/go/runtime/stubs.go b/libgo/go/runtime/stubs.go index 530997b292e0..e00d759b7061 100644 --- a/libgo/go/runtime/stubs.go +++ b/libgo/go/runtime/stubs.go @@ -273,18 +273,6 @@ func checkASM() bool { 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. func osyield()