compiler: optimize string concatenations

runtime.concatstring{2,3,4,5} are just wrappers of concatstrings.
These wrappers don't provide any benefit, at least in the C
calling convention we use, where passing arrays by value isn't an
efficient thing. Change it to always use concatstrings.

Also, the cap field of the slice passed to concatstrings is not
necessary. So change it to pass a pointer and a length directly,
which is more efficient than passing a slice header by value.

Change-Id: I068a5cc83f938d3fa936b8fa5a064aaa0a42d9d1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/182539
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/expressions.cc b/go/expressions.cc
index 766864a..864b62d 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -7442,7 +7442,7 @@
 
 Expression*
 String_concat_expression::do_flatten(Gogo*, Named_object*,
-				     Statement_inserter*)
+				     Statement_inserter* inserter)
 {
   if (this->is_error_expression())
     return this;
@@ -7497,56 +7497,22 @@
     }
   if (buf == NULL)
     buf = Expression::make_nil(loc);
-  Expression* call;
-  switch (this->exprs_->size())
-    {
-    case 0: case 1:
-      go_unreachable();
-
-    case 2: case 3: case 4: case 5:
-      {
-	Expression* len = Expression::make_integer_ul(this->exprs_->size(),
-						      NULL, loc);
-	Array_type* arg_type = Type::make_array_type(type, len);
-	arg_type->set_is_array_incomparable();
-	Expression* arg =
-	  Expression::make_array_composite_literal(arg_type, this->exprs_,
-						   loc);
-	Runtime::Function code;
-	switch (this->exprs_->size())
-	  {
-	  default:
-	    go_unreachable();
-	  case 2:
-	    code = Runtime::CONCATSTRING2;
-	    break;
-	  case 3:
-	    code = Runtime::CONCATSTRING3;
-	    break;
-	  case 4:
-	    code = Runtime::CONCATSTRING4;
-	    break;
-	  case 5:
-	    code = Runtime::CONCATSTRING5;
-	    break;
-	  }
-	call = Runtime::make_call(code, loc, 2, buf, arg);
-      }
-      break;
-
-    default:
-      {
-	Type* arg_type = Type::make_array_type(type, NULL);
-	Slice_construction_expression* sce =
-	  Expression::make_slice_composite_literal(arg_type, this->exprs_,
-						   loc);
-	sce->set_storage_does_not_escape();
-	call = Runtime::make_call(Runtime::CONCATSTRINGS, loc, 2, buf,
-				  sce);
-      }
-      break;
-    }
-
+  go_assert(this->exprs_->size() > 1);
+  Expression* len =
+    Expression::make_integer_ul(this->exprs_->size(), NULL, loc);
+  Array_type* array_type = Type::make_array_type(type, len);
+  array_type->set_is_array_incomparable();
+  Expression* array =
+    Expression::make_array_composite_literal(array_type, this->exprs_,
+                                             loc);
+  Temporary_statement* ts =
+    Statement::make_temporary(array_type, array, loc);
+  inserter->insert(ts);
+  Expression* ref = Expression::make_temporary_reference(ts, loc);
+  ref = Expression::make_unary(OPERATOR_AND, ref, loc);
+	Expression* call =
+    Runtime::make_call(Runtime::CONCATSTRINGS, loc, 3, buf,
+                       ref, len->copy());
   return Expression::make_cast(type, call, loc);
 }
 
diff --git a/go/runtime.def b/go/runtime.def
index 34c86e8..c81ab79 100644
--- a/go/runtime.def
+++ b/go/runtime.def
@@ -36,16 +36,8 @@
 	       R2(RUNE, INT))
 
 // Concatenate strings.
-DEF_GO_RUNTIME(CONCATSTRINGS, "runtime.concatstrings", P2(POINTER, SLICE),
-	       R1(STRING))
-DEF_GO_RUNTIME(CONCATSTRING2, "runtime.concatstring2",
-	       P2(POINTER, ARRAY2STRING), R1(STRING))
-DEF_GO_RUNTIME(CONCATSTRING3, "runtime.concatstring3",
-	       P2(POINTER, ARRAY3STRING), R1(STRING))
-DEF_GO_RUNTIME(CONCATSTRING4, "runtime.concatstring4",
-	       P2(POINTER, ARRAY4STRING), R1(STRING))
-DEF_GO_RUNTIME(CONCATSTRING5, "runtime.concatstring5",
-	       P2(POINTER, ARRAY5STRING), R1(STRING))
+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))
diff --git a/libgo/go/runtime/string.go b/libgo/go/runtime/string.go
index eac94bf..9bcfc99 100644
--- a/libgo/go/runtime/string.go
+++ b/libgo/go/runtime/string.go
@@ -13,10 +13,6 @@
 // themselves, so that the compiler will export them.
 //
 //go:linkname concatstrings runtime.concatstrings
-//go:linkname concatstring2 runtime.concatstring2
-//go:linkname concatstring3 runtime.concatstring3
-//go:linkname concatstring4 runtime.concatstring4
-//go:linkname concatstring5 runtime.concatstring5
 //go:linkname slicebytetostring runtime.slicebytetostring
 //go:linkname slicebytetostringtmp runtime.slicebytetostringtmp
 //go:linkname stringtoslicebyte runtime.stringtoslicebyte
@@ -38,7 +34,9 @@
 // If buf != nil, the compiler has determined that the result does not
 // escape the calling function, so the string data can be stored in buf
 // if small enough.
-func concatstrings(buf *tmpBuf, a []string) string {
+func concatstrings(buf *tmpBuf, p *string, n int) string {
+	var a []string
+	*(*slice)(unsafe.Pointer(&a)) = slice{unsafe.Pointer(p), n, n}
 	// idx := 0
 	l := 0
 	count := 0
@@ -73,22 +71,6 @@
 	return s
 }
 
-func concatstring2(buf *tmpBuf, a [2]string) string {
-	return concatstrings(buf, a[:])
-}
-
-func concatstring3(buf *tmpBuf, a [3]string) string {
-	return concatstrings(buf, a[:])
-}
-
-func concatstring4(buf *tmpBuf, a [4]string) string {
-	return concatstrings(buf, a[:])
-}
-
-func concatstring5(buf *tmpBuf, a [5]string) string {
-	return concatstrings(buf, a[:])
-}
-
 // Buf is a fixed-size buffer for the result,
 // it is not nil if the result does not escape.
 func slicebytetostring(buf *tmpBuf, b []byte) (str string) {