]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: optimize []byte(string1 + string2)
authorPaschalis Tsilias <paschalis.tsilias@gmail.com>
Wed, 13 Sep 2023 09:44:17 +0000 (12:44 +0300)
committerGopher Robot <gobot@golang.org>
Tue, 10 Sep 2024 21:20:57 +0000 (21:20 +0000)
This CL optimizes the compilation of string-to-bytes conversion in the
case of string additions.

Fixes #62407

Change-Id: Ic47df758478e5d061880620025c4ec7dbbff8a64
Reviewed-on: https://go-review.googlesource.com/c/go/+/527935
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
Auto-Submit: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Tim King <taking@google.com>
src/cmd/compile/internal/test/issue62407_test.go [new file with mode: 0644]
src/cmd/compile/internal/typecheck/_builtin/runtime.go
src/cmd/compile/internal/typecheck/builtin.go
src/cmd/compile/internal/walk/convert.go
src/cmd/compile/internal/walk/expr.go
src/cmd/internal/goobj/builtinlist.go
src/runtime/string.go
test/codegen/strings.go

diff --git a/src/cmd/compile/internal/test/issue62407_test.go b/src/cmd/compile/internal/test/issue62407_test.go
new file mode 100644 (file)
index 0000000..d065673
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright 2024 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.
+
+package test
+
+import (
+       "reflect"
+       "testing"
+)
+
+//go:noinline
+func foo() string { return "foo" }
+
+//go:noinline
+func empty() string { return "" }
+
+func TestConcatBytes(t *testing.T) {
+       empty := empty()
+       s := foo()
+       tests := map[string]struct {
+               got  []byte
+               want []byte
+       }{
+               "two empty elements":                 {got: []byte(empty + empty), want: []byte{}},
+               "two nonempty elements":              {got: []byte(s + s), want: []byte("foofoo")},
+               "one empty and one nonempty element": {got: []byte(s + empty), want: []byte("foo")},
+               "multiple empty elements":            {got: []byte(empty + empty + empty + empty + empty + empty), want: []byte{}},
+               "multiple nonempty elements":         {got: []byte("1" + "2" + "3" + "4" + "5" + "6"), want: []byte("123456")},
+       }
+
+       for name, test := range tests {
+               if !reflect.DeepEqual(test.got, test.want) {
+                       t.Errorf("[%s] got: %s, want: %s", name, test.got, test.want)
+               }
+       }
+}
+
+func TestConcatBytesAllocations(t *testing.T) {
+       empty := empty()
+       s := foo()
+       tests := map[string]struct {
+               f      func() []byte
+               allocs float64
+       }{
+               "two empty elements":      {f: func() []byte { return []byte(empty + empty) }, allocs: 0},
+               "multiple empty elements": {f: func() []byte { return []byte(empty + empty + empty + empty + empty + empty) }, allocs: 0},
+
+               "two elements":                       {f: func() []byte { return []byte(s + s) }, allocs: 1},
+               "three elements":                     {f: func() []byte { return []byte(s + s + s) }, allocs: 1},
+               "four elements":                      {f: func() []byte { return []byte(s + s + s + s) }, allocs: 1},
+               "five elements":                      {f: func() []byte { return []byte(s + s + s + s + s) }, allocs: 1},
+               "one empty and one nonempty element": {f: func() []byte { return []byte(s + empty) }, allocs: 1},
+               "two empty and two nonempty element": {f: func() []byte { return []byte(s + empty + s + empty) }, allocs: 1},
+       }
+       for name, test := range tests {
+               allocs := testing.AllocsPerRun(100, func() { test.f() })
+               if allocs != test.allocs {
+                       t.Errorf("concatbytes [%s]: %v allocs, want %v", name, allocs, test.allocs)
+               }
+       }
+}
index 3a5d3576be3e4535d5df0fae7b59c75131e4d625..bf6b58672577bfdb97e9262032f4ff2dcd3f901a 100644 (file)
@@ -71,6 +71,12 @@ func concatstring4(*[32]byte, string, string, string, string) string
 func concatstring5(*[32]byte, string, string, string, string, string) string
 func concatstrings(*[32]byte, []string) string
 
+func concatbyte2(string, string) []byte
+func concatbyte3(string, string, string) []byte
+func concatbyte4(string, string, string, string) []byte
+func concatbyte5(string, string, string, string, string) []byte
+func concatbytes([]string) []byte
+
 func cmpstring(string, string) int
 func intstring(*[4]byte, int64) string
 func slicebytetostring(buf *[32]byte, ptr *byte, n int) string
index 2f43b1d01ca04ddc20733495fbadeb79bd953a3d..b4d2ff73ecd10f9045798927a815830cfa66a600 100644 (file)
@@ -76,168 +76,173 @@ var runtimeDecls = [...]struct {
        {"concatstring4", funcTag, 36},
        {"concatstring5", funcTag, 37},
        {"concatstrings", funcTag, 39},
-       {"cmpstring", funcTag, 40},
-       {"intstring", funcTag, 43},
-       {"slicebytetostring", funcTag, 44},
-       {"slicebytetostringtmp", funcTag, 45},
-       {"slicerunetostring", funcTag, 48},
-       {"stringtoslicebyte", funcTag, 50},
-       {"stringtoslicerune", funcTag, 53},
-       {"slicecopy", funcTag, 54},
-       {"decoderune", funcTag, 55},
-       {"countrunes", funcTag, 56},
-       {"convT", funcTag, 57},
-       {"convTnoptr", funcTag, 57},
-       {"convT16", funcTag, 59},
-       {"convT32", funcTag, 61},
-       {"convT64", funcTag, 62},
-       {"convTstring", funcTag, 63},
-       {"convTslice", funcTag, 66},
-       {"assertE2I", funcTag, 67},
-       {"assertE2I2", funcTag, 67},
-       {"panicdottypeE", funcTag, 68},
-       {"panicdottypeI", funcTag, 68},
-       {"panicnildottype", funcTag, 69},
-       {"typeAssert", funcTag, 67},
-       {"interfaceSwitch", funcTag, 70},
-       {"ifaceeq", funcTag, 72},
-       {"efaceeq", funcTag, 72},
-       {"panicrangestate", funcTag, 73},
-       {"deferrangefunc", funcTag, 74},
-       {"rand32", funcTag, 75},
-       {"makemap64", funcTag, 77},
-       {"makemap", funcTag, 78},
-       {"makemap_small", funcTag, 79},
-       {"mapaccess1", funcTag, 80},
-       {"mapaccess1_fast32", funcTag, 81},
-       {"mapaccess1_fast64", funcTag, 82},
-       {"mapaccess1_faststr", funcTag, 83},
-       {"mapaccess1_fat", funcTag, 84},
-       {"mapaccess2", funcTag, 85},
-       {"mapaccess2_fast32", funcTag, 86},
-       {"mapaccess2_fast64", funcTag, 87},
-       {"mapaccess2_faststr", funcTag, 88},
-       {"mapaccess2_fat", funcTag, 89},
-       {"mapassign", funcTag, 80},
-       {"mapassign_fast32", funcTag, 81},
-       {"mapassign_fast32ptr", funcTag, 90},
-       {"mapassign_fast64", funcTag, 82},
-       {"mapassign_fast64ptr", funcTag, 90},
-       {"mapassign_faststr", funcTag, 83},
-       {"mapiterinit", funcTag, 91},
-       {"mapdelete", funcTag, 91},
-       {"mapdelete_fast32", funcTag, 92},
-       {"mapdelete_fast64", funcTag, 93},
-       {"mapdelete_faststr", funcTag, 94},
-       {"mapiternext", funcTag, 95},
-       {"mapclear", funcTag, 96},
-       {"makechan64", funcTag, 98},
-       {"makechan", funcTag, 99},
-       {"chanrecv1", funcTag, 101},
-       {"chanrecv2", funcTag, 102},
-       {"chansend1", funcTag, 104},
-       {"closechan", funcTag, 105},
-       {"chanlen", funcTag, 106},
-       {"chancap", funcTag, 106},
-       {"writeBarrier", varTag, 108},
-       {"typedmemmove", funcTag, 109},
-       {"typedmemclr", funcTag, 110},
-       {"typedslicecopy", funcTag, 111},
-       {"selectnbsend", funcTag, 112},
-       {"selectnbrecv", funcTag, 113},
-       {"selectsetpc", funcTag, 114},
-       {"selectgo", funcTag, 115},
+       {"concatbyte2", funcTag, 41},
+       {"concatbyte3", funcTag, 42},
+       {"concatbyte4", funcTag, 43},
+       {"concatbyte5", funcTag, 44},
+       {"concatbytes", funcTag, 45},
+       {"cmpstring", funcTag, 46},
+       {"intstring", funcTag, 49},
+       {"slicebytetostring", funcTag, 50},
+       {"slicebytetostringtmp", funcTag, 51},
+       {"slicerunetostring", funcTag, 54},
+       {"stringtoslicebyte", funcTag, 55},
+       {"stringtoslicerune", funcTag, 58},
+       {"slicecopy", funcTag, 59},
+       {"decoderune", funcTag, 60},
+       {"countrunes", funcTag, 61},
+       {"convT", funcTag, 62},
+       {"convTnoptr", funcTag, 62},
+       {"convT16", funcTag, 64},
+       {"convT32", funcTag, 66},
+       {"convT64", funcTag, 67},
+       {"convTstring", funcTag, 68},
+       {"convTslice", funcTag, 71},
+       {"assertE2I", funcTag, 72},
+       {"assertE2I2", funcTag, 72},
+       {"panicdottypeE", funcTag, 73},
+       {"panicdottypeI", funcTag, 73},
+       {"panicnildottype", funcTag, 74},
+       {"typeAssert", funcTag, 72},
+       {"interfaceSwitch", funcTag, 75},
+       {"ifaceeq", funcTag, 77},
+       {"efaceeq", funcTag, 77},
+       {"panicrangestate", funcTag, 78},
+       {"deferrangefunc", funcTag, 79},
+       {"rand32", funcTag, 80},
+       {"makemap64", funcTag, 82},
+       {"makemap", funcTag, 83},
+       {"makemap_small", funcTag, 84},
+       {"mapaccess1", funcTag, 85},
+       {"mapaccess1_fast32", funcTag, 86},
+       {"mapaccess1_fast64", funcTag, 87},
+       {"mapaccess1_faststr", funcTag, 88},
+       {"mapaccess1_fat", funcTag, 89},
+       {"mapaccess2", funcTag, 90},
+       {"mapaccess2_fast32", funcTag, 91},
+       {"mapaccess2_fast64", funcTag, 92},
+       {"mapaccess2_faststr", funcTag, 93},
+       {"mapaccess2_fat", funcTag, 94},
+       {"mapassign", funcTag, 85},
+       {"mapassign_fast32", funcTag, 86},
+       {"mapassign_fast32ptr", funcTag, 95},
+       {"mapassign_fast64", funcTag, 87},
+       {"mapassign_fast64ptr", funcTag, 95},
+       {"mapassign_faststr", funcTag, 88},
+       {"mapiterinit", funcTag, 96},
+       {"mapdelete", funcTag, 96},
+       {"mapdelete_fast32", funcTag, 97},
+       {"mapdelete_fast64", funcTag, 98},
+       {"mapdelete_faststr", funcTag, 99},
+       {"mapiternext", funcTag, 100},
+       {"mapclear", funcTag, 101},
+       {"makechan64", funcTag, 103},
+       {"makechan", funcTag, 104},
+       {"chanrecv1", funcTag, 106},
+       {"chanrecv2", funcTag, 107},
+       {"chansend1", funcTag, 109},
+       {"closechan", funcTag, 110},
+       {"chanlen", funcTag, 111},
+       {"chancap", funcTag, 111},
+       {"writeBarrier", varTag, 113},
+       {"typedmemmove", funcTag, 114},
+       {"typedmemclr", funcTag, 115},
+       {"typedslicecopy", funcTag, 116},
+       {"selectnbsend", funcTag, 117},
+       {"selectnbrecv", funcTag, 118},
+       {"selectsetpc", funcTag, 119},
+       {"selectgo", funcTag, 120},
        {"block", funcTag, 9},
-       {"makeslice", funcTag, 116},
-       {"makeslice64", funcTag, 117},
-       {"makeslicecopy", funcTag, 118},
-       {"growslice", funcTag, 120},
-       {"unsafeslicecheckptr", funcTag, 121},
+       {"makeslice", funcTag, 121},
+       {"makeslice64", funcTag, 122},
+       {"makeslicecopy", funcTag, 123},
+       {"growslice", funcTag, 125},
+       {"unsafeslicecheckptr", funcTag, 126},
        {"panicunsafeslicelen", funcTag, 9},
        {"panicunsafeslicenilptr", funcTag, 9},
-       {"unsafestringcheckptr", funcTag, 122},
+       {"unsafestringcheckptr", funcTag, 127},
        {"panicunsafestringlen", funcTag, 9},
        {"panicunsafestringnilptr", funcTag, 9},
-       {"memmove", funcTag, 123},
-       {"memclrNoHeapPointers", funcTag, 124},
-       {"memclrHasPointers", funcTag, 124},
-       {"memequal", funcTag, 125},
-       {"memequal0", funcTag, 126},
-       {"memequal8", funcTag, 126},
-       {"memequal16", funcTag, 126},
-       {"memequal32", funcTag, 126},
-       {"memequal64", funcTag, 126},
-       {"memequal128", funcTag, 126},
-       {"f32equal", funcTag, 127},
-       {"f64equal", funcTag, 127},
-       {"c64equal", funcTag, 127},
-       {"c128equal", funcTag, 127},
-       {"strequal", funcTag, 127},
-       {"interequal", funcTag, 127},
-       {"nilinterequal", funcTag, 127},
-       {"memhash", funcTag, 128},
-       {"memhash0", funcTag, 129},
-       {"memhash8", funcTag, 129},
-       {"memhash16", funcTag, 129},
-       {"memhash32", funcTag, 129},
-       {"memhash64", funcTag, 129},
-       {"memhash128", funcTag, 129},
-       {"f32hash", funcTag, 130},
-       {"f64hash", funcTag, 130},
-       {"c64hash", funcTag, 130},
-       {"c128hash", funcTag, 130},
-       {"strhash", funcTag, 130},
-       {"interhash", funcTag, 130},
-       {"nilinterhash", funcTag, 130},
-       {"int64div", funcTag, 131},
-       {"uint64div", funcTag, 132},
-       {"int64mod", funcTag, 131},
-       {"uint64mod", funcTag, 132},
-       {"float64toint64", funcTag, 133},
-       {"float64touint64", funcTag, 134},
-       {"float64touint32", funcTag, 135},
-       {"int64tofloat64", funcTag, 136},
-       {"int64tofloat32", funcTag, 138},
-       {"uint64tofloat64", funcTag, 139},
-       {"uint64tofloat32", funcTag, 140},
-       {"uint32tofloat64", funcTag, 141},
-       {"complex128div", funcTag, 142},
-       {"getcallerpc", funcTag, 143},
-       {"getcallersp", funcTag, 143},
+       {"memmove", funcTag, 128},
+       {"memclrNoHeapPointers", funcTag, 129},
+       {"memclrHasPointers", funcTag, 129},
+       {"memequal", funcTag, 130},
+       {"memequal0", funcTag, 131},
+       {"memequal8", funcTag, 131},
+       {"memequal16", funcTag, 131},
+       {"memequal32", funcTag, 131},
+       {"memequal64", funcTag, 131},
+       {"memequal128", funcTag, 131},
+       {"f32equal", funcTag, 132},
+       {"f64equal", funcTag, 132},
+       {"c64equal", funcTag, 132},
+       {"c128equal", funcTag, 132},
+       {"strequal", funcTag, 132},
+       {"interequal", funcTag, 132},
+       {"nilinterequal", funcTag, 132},
+       {"memhash", funcTag, 133},
+       {"memhash0", funcTag, 134},
+       {"memhash8", funcTag, 134},
+       {"memhash16", funcTag, 134},
+       {"memhash32", funcTag, 134},
+       {"memhash64", funcTag, 134},
+       {"memhash128", funcTag, 134},
+       {"f32hash", funcTag, 135},
+       {"f64hash", funcTag, 135},
+       {"c64hash", funcTag, 135},
+       {"c128hash", funcTag, 135},
+       {"strhash", funcTag, 135},
+       {"interhash", funcTag, 135},
+       {"nilinterhash", funcTag, 135},
+       {"int64div", funcTag, 136},
+       {"uint64div", funcTag, 137},
+       {"int64mod", funcTag, 136},
+       {"uint64mod", funcTag, 137},
+       {"float64toint64", funcTag, 138},
+       {"float64touint64", funcTag, 139},
+       {"float64touint32", funcTag, 140},
+       {"int64tofloat64", funcTag, 141},
+       {"int64tofloat32", funcTag, 143},
+       {"uint64tofloat64", funcTag, 144},
+       {"uint64tofloat32", funcTag, 145},
+       {"uint32tofloat64", funcTag, 146},
+       {"complex128div", funcTag, 147},
+       {"getcallerpc", funcTag, 148},
+       {"getcallersp", funcTag, 148},
        {"racefuncenter", funcTag, 31},
        {"racefuncexit", funcTag, 9},
        {"raceread", funcTag, 31},
        {"racewrite", funcTag, 31},
-       {"racereadrange", funcTag, 144},
-       {"racewriterange", funcTag, 144},
-       {"msanread", funcTag, 144},
-       {"msanwrite", funcTag, 144},
-       {"msanmove", funcTag, 145},
-       {"asanread", funcTag, 144},
-       {"asanwrite", funcTag, 144},
-       {"checkptrAlignment", funcTag, 146},
-       {"checkptrArithmetic", funcTag, 148},
-       {"libfuzzerTraceCmp1", funcTag, 149},
-       {"libfuzzerTraceCmp2", funcTag, 150},
-       {"libfuzzerTraceCmp4", funcTag, 151},
-       {"libfuzzerTraceCmp8", funcTag, 152},
-       {"libfuzzerTraceConstCmp1", funcTag, 149},
-       {"libfuzzerTraceConstCmp2", funcTag, 150},
-       {"libfuzzerTraceConstCmp4", funcTag, 151},
-       {"libfuzzerTraceConstCmp8", funcTag, 152},
-       {"libfuzzerHookStrCmp", funcTag, 153},
-       {"libfuzzerHookEqualFold", funcTag, 153},
-       {"addCovMeta", funcTag, 155},
+       {"racereadrange", funcTag, 149},
+       {"racewriterange", funcTag, 149},
+       {"msanread", funcTag, 149},
+       {"msanwrite", funcTag, 149},
+       {"msanmove", funcTag, 150},
+       {"asanread", funcTag, 149},
+       {"asanwrite", funcTag, 149},
+       {"checkptrAlignment", funcTag, 151},
+       {"checkptrArithmetic", funcTag, 153},
+       {"libfuzzerTraceCmp1", funcTag, 154},
+       {"libfuzzerTraceCmp2", funcTag, 155},
+       {"libfuzzerTraceCmp4", funcTag, 156},
+       {"libfuzzerTraceCmp8", funcTag, 157},
+       {"libfuzzerTraceConstCmp1", funcTag, 154},
+       {"libfuzzerTraceConstCmp2", funcTag, 155},
+       {"libfuzzerTraceConstCmp4", funcTag, 156},
+       {"libfuzzerTraceConstCmp8", funcTag, 157},
+       {"libfuzzerHookStrCmp", funcTag, 158},
+       {"libfuzzerHookEqualFold", funcTag, 158},
+       {"addCovMeta", funcTag, 160},
        {"x86HasPOPCNT", varTag, 6},
        {"x86HasSSE41", varTag, 6},
        {"x86HasFMA", varTag, 6},
        {"armHasVFPv4", varTag, 6},
        {"arm64HasATOMICS", varTag, 6},
-       {"asanregisterglobals", funcTag, 124},
+       {"asanregisterglobals", funcTag, 129},
 }
 
 func runtimeTypes() []*types.Type {
-       var typs [156]*types.Type
+       var typs [161]*types.Type
        typs[0] = types.ByteType
        typs[1] = types.NewPtr(typs[0])
        typs[2] = types.Types[types.TANY]
@@ -278,122 +283,127 @@ func runtimeTypes() []*types.Type {
        typs[37] = newSig(params(typs[33], typs[28], typs[28], typs[28], typs[28], typs[28]), params(typs[28]))
        typs[38] = types.NewSlice(typs[28])
        typs[39] = newSig(params(typs[33], typs[38]), params(typs[28]))
-       typs[40] = newSig(params(typs[28], typs[28]), params(typs[15]))
-       typs[41] = types.NewArray(typs[0], 4)
-       typs[42] = types.NewPtr(typs[41])
-       typs[43] = newSig(params(typs[42], typs[22]), params(typs[28]))
-       typs[44] = newSig(params(typs[33], typs[1], typs[15]), params(typs[28]))
-       typs[45] = newSig(params(typs[1], typs[15]), params(typs[28]))
-       typs[46] = types.RuneType
-       typs[47] = types.NewSlice(typs[46])
-       typs[48] = newSig(params(typs[33], typs[47]), params(typs[28]))
-       typs[49] = types.NewSlice(typs[0])
-       typs[50] = newSig(params(typs[33], typs[28]), params(typs[49]))
-       typs[51] = types.NewArray(typs[46], 32)
-       typs[52] = types.NewPtr(typs[51])
-       typs[53] = newSig(params(typs[52], typs[28]), params(typs[47]))
-       typs[54] = newSig(params(typs[3], typs[15], typs[3], typs[15], typs[5]), params(typs[15]))
-       typs[55] = newSig(params(typs[28], typs[15]), params(typs[46], typs[15]))
-       typs[56] = newSig(params(typs[28]), params(typs[15]))
-       typs[57] = newSig(params(typs[1], typs[3]), params(typs[7]))
-       typs[58] = types.Types[types.TUINT16]
-       typs[59] = newSig(params(typs[58]), params(typs[7]))
-       typs[60] = types.Types[types.TUINT32]
-       typs[61] = newSig(params(typs[60]), params(typs[7]))
-       typs[62] = newSig(params(typs[24]), params(typs[7]))
-       typs[63] = newSig(params(typs[28]), params(typs[7]))
-       typs[64] = types.Types[types.TUINT8]
-       typs[65] = types.NewSlice(typs[64])
+       typs[40] = types.NewSlice(typs[0])
+       typs[41] = newSig(params(typs[28], typs[28]), params(typs[40]))
+       typs[42] = newSig(params(typs[28], typs[28], typs[28]), params(typs[40]))
+       typs[43] = newSig(params(typs[28], typs[28], typs[28], typs[28]), params(typs[40]))
+       typs[44] = newSig(params(typs[28], typs[28], typs[28], typs[28], typs[28]), params(typs[40]))
+       typs[45] = newSig(params(typs[38]), params(typs[40]))
+       typs[46] = newSig(params(typs[28], typs[28]), params(typs[15]))
+       typs[47] = types.NewArray(typs[0], 4)
+       typs[48] = types.NewPtr(typs[47])
+       typs[49] = newSig(params(typs[48], typs[22]), params(typs[28]))
+       typs[50] = newSig(params(typs[33], typs[1], typs[15]), params(typs[28]))
+       typs[51] = newSig(params(typs[1], typs[15]), params(typs[28]))
+       typs[52] = types.RuneType
+       typs[53] = types.NewSlice(typs[52])
+       typs[54] = newSig(params(typs[33], typs[53]), params(typs[28]))
+       typs[55] = newSig(params(typs[33], typs[28]), params(typs[40]))
+       typs[56] = types.NewArray(typs[52], 32)
+       typs[57] = types.NewPtr(typs[56])
+       typs[58] = newSig(params(typs[57], typs[28]), params(typs[53]))
+       typs[59] = newSig(params(typs[3], typs[15], typs[3], typs[15], typs[5]), params(typs[15]))
+       typs[60] = newSig(params(typs[28], typs[15]), params(typs[52], typs[15]))
+       typs[61] = newSig(params(typs[28]), params(typs[15]))
+       typs[62] = newSig(params(typs[1], typs[3]), params(typs[7]))
+       typs[63] = types.Types[types.TUINT16]
+       typs[64] = newSig(params(typs[63]), params(typs[7]))
+       typs[65] = types.Types[types.TUINT32]
        typs[66] = newSig(params(typs[65]), params(typs[7]))
-       typs[67] = newSig(params(typs[1], typs[1]), params(typs[1]))
-       typs[68] = newSig(params(typs[1], typs[1], typs[1]), nil)
-       typs[69] = newSig(params(typs[1]), nil)
-       typs[70] = newSig(params(typs[1], typs[1]), params(typs[15], typs[1]))
-       typs[71] = types.NewPtr(typs[5])
-       typs[72] = newSig(params(typs[71], typs[7], typs[7]), params(typs[6]))
-       typs[73] = newSig(params(typs[15]), nil)
-       typs[74] = newSig(nil, params(typs[10]))
-       typs[75] = newSig(nil, params(typs[60]))
-       typs[76] = types.NewMap(typs[2], typs[2])
-       typs[77] = newSig(params(typs[1], typs[22], typs[3]), params(typs[76]))
-       typs[78] = newSig(params(typs[1], typs[15], typs[3]), params(typs[76]))
-       typs[79] = newSig(nil, params(typs[76]))
-       typs[80] = newSig(params(typs[1], typs[76], typs[3]), params(typs[3]))
-       typs[81] = newSig(params(typs[1], typs[76], typs[60]), params(typs[3]))
-       typs[82] = newSig(params(typs[1], typs[76], typs[24]), params(typs[3]))
-       typs[83] = newSig(params(typs[1], typs[76], typs[28]), params(typs[3]))
-       typs[84] = newSig(params(typs[1], typs[76], typs[3], typs[1]), params(typs[3]))
-       typs[85] = newSig(params(typs[1], typs[76], typs[3]), params(typs[3], typs[6]))
-       typs[86] = newSig(params(typs[1], typs[76], typs[60]), params(typs[3], typs[6]))
-       typs[87] = newSig(params(typs[1], typs[76], typs[24]), params(typs[3], typs[6]))
-       typs[88] = newSig(params(typs[1], typs[76], typs[28]), params(typs[3], typs[6]))
-       typs[89] = newSig(params(typs[1], typs[76], typs[3], typs[1]), params(typs[3], typs[6]))
-       typs[90] = newSig(params(typs[1], typs[76], typs[7]), params(typs[3]))
-       typs[91] = newSig(params(typs[1], typs[76], typs[3]), nil)
-       typs[92] = newSig(params(typs[1], typs[76], typs[60]), nil)
-       typs[93] = newSig(params(typs[1], typs[76], typs[24]), nil)
-       typs[94] = newSig(params(typs[1], typs[76], typs[28]), nil)
-       typs[95] = newSig(params(typs[3]), nil)
-       typs[96] = newSig(params(typs[1], typs[76]), nil)
-       typs[97] = types.NewChan(typs[2], types.Cboth)
-       typs[98] = newSig(params(typs[1], typs[22]), params(typs[97]))
-       typs[99] = newSig(params(typs[1], typs[15]), params(typs[97]))
-       typs[100] = types.NewChan(typs[2], types.Crecv)
-       typs[101] = newSig(params(typs[100], typs[3]), nil)
-       typs[102] = newSig(params(typs[100], typs[3]), params(typs[6]))
-       typs[103] = types.NewChan(typs[2], types.Csend)
-       typs[104] = newSig(params(typs[103], typs[3]), nil)
-       typs[105] = newSig(params(typs[103]), nil)
-       typs[106] = newSig(params(typs[2]), params(typs[15]))
-       typs[107] = types.NewArray(typs[0], 3)
-       typs[108] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[107]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
-       typs[109] = newSig(params(typs[1], typs[3], typs[3]), nil)
-       typs[110] = newSig(params(typs[1], typs[3]), nil)
-       typs[111] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
-       typs[112] = newSig(params(typs[103], typs[3]), params(typs[6]))
-       typs[113] = newSig(params(typs[3], typs[100]), params(typs[6], typs[6]))
-       typs[114] = newSig(params(typs[71]), nil)
-       typs[115] = newSig(params(typs[1], typs[1], typs[71], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
-       typs[116] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
-       typs[117] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
-       typs[118] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
-       typs[119] = types.NewSlice(typs[2])
-       typs[120] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[119]))
-       typs[121] = newSig(params(typs[1], typs[7], typs[22]), nil)
-       typs[122] = newSig(params(typs[7], typs[22]), nil)
-       typs[123] = newSig(params(typs[3], typs[3], typs[5]), nil)
-       typs[124] = newSig(params(typs[7], typs[5]), nil)
-       typs[125] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
-       typs[126] = newSig(params(typs[3], typs[3]), params(typs[6]))
-       typs[127] = newSig(params(typs[7], typs[7]), params(typs[6]))
-       typs[128] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5]))
-       typs[129] = newSig(params(typs[7], typs[5]), params(typs[5]))
-       typs[130] = newSig(params(typs[3], typs[5]), params(typs[5]))
-       typs[131] = newSig(params(typs[22], typs[22]), params(typs[22]))
-       typs[132] = newSig(params(typs[24], typs[24]), params(typs[24]))
-       typs[133] = newSig(params(typs[20]), params(typs[22]))
-       typs[134] = newSig(params(typs[20]), params(typs[24]))
-       typs[135] = newSig(params(typs[20]), params(typs[60]))
-       typs[136] = newSig(params(typs[22]), params(typs[20]))
-       typs[137] = types.Types[types.TFLOAT32]
-       typs[138] = newSig(params(typs[22]), params(typs[137]))
-       typs[139] = newSig(params(typs[24]), params(typs[20]))
-       typs[140] = newSig(params(typs[24]), params(typs[137]))
-       typs[141] = newSig(params(typs[60]), params(typs[20]))
-       typs[142] = newSig(params(typs[26], typs[26]), params(typs[26]))
-       typs[143] = newSig(nil, params(typs[5]))
-       typs[144] = newSig(params(typs[5], typs[5]), nil)
-       typs[145] = newSig(params(typs[5], typs[5], typs[5]), nil)
-       typs[146] = newSig(params(typs[7], typs[1], typs[5]), nil)
-       typs[147] = types.NewSlice(typs[7])
-       typs[148] = newSig(params(typs[7], typs[147]), nil)
-       typs[149] = newSig(params(typs[64], typs[64], typs[17]), nil)
-       typs[150] = newSig(params(typs[58], typs[58], typs[17]), nil)
-       typs[151] = newSig(params(typs[60], typs[60], typs[17]), nil)
-       typs[152] = newSig(params(typs[24], typs[24], typs[17]), nil)
-       typs[153] = newSig(params(typs[28], typs[28], typs[17]), nil)
-       typs[154] = types.NewArray(typs[0], 16)
-       typs[155] = newSig(params(typs[7], typs[60], typs[154], typs[28], typs[15], typs[64], typs[64]), params(typs[60]))
+       typs[67] = newSig(params(typs[24]), params(typs[7]))
+       typs[68] = newSig(params(typs[28]), params(typs[7]))
+       typs[69] = types.Types[types.TUINT8]
+       typs[70] = types.NewSlice(typs[69])
+       typs[71] = newSig(params(typs[70]), params(typs[7]))
+       typs[72] = newSig(params(typs[1], typs[1]), params(typs[1]))
+       typs[73] = newSig(params(typs[1], typs[1], typs[1]), nil)
+       typs[74] = newSig(params(typs[1]), nil)
+       typs[75] = newSig(params(typs[1], typs[1]), params(typs[15], typs[1]))
+       typs[76] = types.NewPtr(typs[5])
+       typs[77] = newSig(params(typs[76], typs[7], typs[7]), params(typs[6]))
+       typs[78] = newSig(params(typs[15]), nil)
+       typs[79] = newSig(nil, params(typs[10]))
+       typs[80] = newSig(nil, params(typs[65]))
+       typs[81] = types.NewMap(typs[2], typs[2])
+       typs[82] = newSig(params(typs[1], typs[22], typs[3]), params(typs[81]))
+       typs[83] = newSig(params(typs[1], typs[15], typs[3]), params(typs[81]))
+       typs[84] = newSig(nil, params(typs[81]))
+       typs[85] = newSig(params(typs[1], typs[81], typs[3]), params(typs[3]))
+       typs[86] = newSig(params(typs[1], typs[81], typs[65]), params(typs[3]))
+       typs[87] = newSig(params(typs[1], typs[81], typs[24]), params(typs[3]))
+       typs[88] = newSig(params(typs[1], typs[81], typs[28]), params(typs[3]))
+       typs[89] = newSig(params(typs[1], typs[81], typs[3], typs[1]), params(typs[3]))
+       typs[90] = newSig(params(typs[1], typs[81], typs[3]), params(typs[3], typs[6]))
+       typs[91] = newSig(params(typs[1], typs[81], typs[65]), params(typs[3], typs[6]))
+       typs[92] = newSig(params(typs[1], typs[81], typs[24]), params(typs[3], typs[6]))
+       typs[93] = newSig(params(typs[1], typs[81], typs[28]), params(typs[3], typs[6]))
+       typs[94] = newSig(params(typs[1], typs[81], typs[3], typs[1]), params(typs[3], typs[6]))
+       typs[95] = newSig(params(typs[1], typs[81], typs[7]), params(typs[3]))
+       typs[96] = newSig(params(typs[1], typs[81], typs[3]), nil)
+       typs[97] = newSig(params(typs[1], typs[81], typs[65]), nil)
+       typs[98] = newSig(params(typs[1], typs[81], typs[24]), nil)
+       typs[99] = newSig(params(typs[1], typs[81], typs[28]), nil)
+       typs[100] = newSig(params(typs[3]), nil)
+       typs[101] = newSig(params(typs[1], typs[81]), nil)
+       typs[102] = types.NewChan(typs[2], types.Cboth)
+       typs[103] = newSig(params(typs[1], typs[22]), params(typs[102]))
+       typs[104] = newSig(params(typs[1], typs[15]), params(typs[102]))
+       typs[105] = types.NewChan(typs[2], types.Crecv)
+       typs[106] = newSig(params(typs[105], typs[3]), nil)
+       typs[107] = newSig(params(typs[105], typs[3]), params(typs[6]))
+       typs[108] = types.NewChan(typs[2], types.Csend)
+       typs[109] = newSig(params(typs[108], typs[3]), nil)
+       typs[110] = newSig(params(typs[108]), nil)
+       typs[111] = newSig(params(typs[2]), params(typs[15]))
+       typs[112] = types.NewArray(typs[0], 3)
+       typs[113] = types.NewStruct([]*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[112]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
+       typs[114] = newSig(params(typs[1], typs[3], typs[3]), nil)
+       typs[115] = newSig(params(typs[1], typs[3]), nil)
+       typs[116] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
+       typs[117] = newSig(params(typs[108], typs[3]), params(typs[6]))
+       typs[118] = newSig(params(typs[3], typs[105]), params(typs[6], typs[6]))
+       typs[119] = newSig(params(typs[76]), nil)
+       typs[120] = newSig(params(typs[1], typs[1], typs[76], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
+       typs[121] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
+       typs[122] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
+       typs[123] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
+       typs[124] = types.NewSlice(typs[2])
+       typs[125] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[124]))
+       typs[126] = newSig(params(typs[1], typs[7], typs[22]), nil)
+       typs[127] = newSig(params(typs[7], typs[22]), nil)
+       typs[128] = newSig(params(typs[3], typs[3], typs[5]), nil)
+       typs[129] = newSig(params(typs[7], typs[5]), nil)
+       typs[130] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
+       typs[131] = newSig(params(typs[3], typs[3]), params(typs[6]))
+       typs[132] = newSig(params(typs[7], typs[7]), params(typs[6]))
+       typs[133] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5]))
+       typs[134] = newSig(params(typs[7], typs[5]), params(typs[5]))
+       typs[135] = newSig(params(typs[3], typs[5]), params(typs[5]))
+       typs[136] = newSig(params(typs[22], typs[22]), params(typs[22]))
+       typs[137] = newSig(params(typs[24], typs[24]), params(typs[24]))
+       typs[138] = newSig(params(typs[20]), params(typs[22]))
+       typs[139] = newSig(params(typs[20]), params(typs[24]))
+       typs[140] = newSig(params(typs[20]), params(typs[65]))
+       typs[141] = newSig(params(typs[22]), params(typs[20]))
+       typs[142] = types.Types[types.TFLOAT32]
+       typs[143] = newSig(params(typs[22]), params(typs[142]))
+       typs[144] = newSig(params(typs[24]), params(typs[20]))
+       typs[145] = newSig(params(typs[24]), params(typs[142]))
+       typs[146] = newSig(params(typs[65]), params(typs[20]))
+       typs[147] = newSig(params(typs[26], typs[26]), params(typs[26]))
+       typs[148] = newSig(nil, params(typs[5]))
+       typs[149] = newSig(params(typs[5], typs[5]), nil)
+       typs[150] = newSig(params(typs[5], typs[5], typs[5]), nil)
+       typs[151] = newSig(params(typs[7], typs[1], typs[5]), nil)
+       typs[152] = types.NewSlice(typs[7])
+       typs[153] = newSig(params(typs[7], typs[152]), nil)
+       typs[154] = newSig(params(typs[69], typs[69], typs[17]), nil)
+       typs[155] = newSig(params(typs[63], typs[63], typs[17]), nil)
+       typs[156] = newSig(params(typs[65], typs[65], typs[17]), nil)
+       typs[157] = newSig(params(typs[24], typs[24], typs[17]), nil)
+       typs[158] = newSig(params(typs[28], typs[28], typs[17]), nil)
+       typs[159] = types.NewArray(typs[0], 16)
+       typs[160] = newSig(params(typs[7], typs[65], typs[159], typs[28], typs[15], typs[69], typs[69]), params(typs[65]))
        return typs[:]
 }
 
index 280b3b65e82dc63751c7d392d302e46a95f20128..31182336970bd548f6720b7dbbb46c50ad6dc09d 100644 (file)
@@ -270,6 +270,11 @@ func walkRuneToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
 // walkStringToBytes walks an OSTR2BYTES node.
 func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
        s := n.X
+
+       if expr, ok := s.(*ir.AddStringExpr); ok {
+               return walkAddString(n.Type(), expr, init)
+       }
+
        if ir.IsConst(s, constant.String) {
                sc := ir.StringVal(s)
 
index f73b5d95034620bfeed697ff45e291d410e0f44f..8c36e03aa07a81f01e46115c50e1b4705f7d9ec0 100644 (file)
@@ -273,7 +273,7 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
                return walkNew(n, init)
 
        case ir.OADDSTR:
-               return walkAddString(n.(*ir.AddStringExpr), init)
+               return walkAddString(n.Type(), n.(*ir.AddStringExpr), init)
 
        case ir.OAPPEND:
                // order should make sure we only see OAS(node, OAPPEND), which we handle above.
@@ -464,49 +464,64 @@ func copyExpr(n ir.Node, t *types.Type, init *ir.Nodes) ir.Node {
        return l
 }
 
-func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node {
+func walkAddString(typ *types.Type, n *ir.AddStringExpr, init *ir.Nodes) ir.Node {
        c := len(n.List)
 
        if c < 2 {
                base.Fatalf("walkAddString count %d too small", c)
        }
 
-       buf := typecheck.NodNil()
-       if n.Esc() == ir.EscNone {
-               sz := int64(0)
-               for _, n1 := range n.List {
-                       if n1.Op() == ir.OLITERAL {
-                               sz += int64(len(ir.StringVal(n1)))
+       // list of string arguments
+       var args []ir.Node
+
+       var fn, fnsmall, fnbig string
+
+       switch {
+       default:
+               base.FatalfAt(n.Pos(), "unexpected type: %v", typ)
+       case typ.IsString():
+               buf := typecheck.NodNil()
+               if n.Esc() == ir.EscNone {
+                       sz := int64(0)
+                       for _, n1 := range n.List {
+                               if n1.Op() == ir.OLITERAL {
+                                       sz += int64(len(ir.StringVal(n1)))
+                               }
                        }
-               }
 
-               // Don't allocate the buffer if the result won't fit.
-               if sz < tmpstringbufsize {
-                       // Create temporary buffer for result string on stack.
-                       buf = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
+                       // Don't allocate the buffer if the result won't fit.
+                       if sz < tmpstringbufsize {
+                               // Create temporary buffer for result string on stack.
+                               buf = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
+                       }
                }
-       }
 
-       // build list of string arguments
-       args := []ir.Node{buf}
-       for _, n2 := range n.List {
-               args = append(args, typecheck.Conv(n2, types.Types[types.TSTRING]))
+               args = []ir.Node{buf}
+               fnsmall, fnbig = "concatstring%d", "concatstrings"
+       case typ.IsSlice() && typ.Elem().IsKind(types.TUINT8): // Optimize []byte(str1+str2+...)
+               fnsmall, fnbig = "concatbyte%d", "concatbytes"
        }
 
-       var fn string
        if c <= 5 {
                // small numbers of strings use direct runtime helpers.
                // note: order.expr knows this cutoff too.
-               fn = fmt.Sprintf("concatstring%d", c)
+               fn = fmt.Sprintf(fnsmall, c)
+
+               for _, n2 := range n.List {
+                       args = append(args, typecheck.Conv(n2, types.Types[types.TSTRING]))
+               }
        } else {
                // large numbers of strings are passed to the runtime as a slice.
-               fn = "concatstrings"
-
+               fn = fnbig
                t := types.NewSlice(types.Types[types.TSTRING])
-               // args[1:] to skip buf arg
-               slice := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, t, args[1:])
+
+               slargs := make([]ir.Node, len(n.List))
+               for i, n2 := range n.List {
+                       slargs[i] = typecheck.Conv(n2, types.Types[types.TSTRING])
+               }
+               slice := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, t, slargs)
                slice.Prealloc = n.Prealloc
-               args = []ir.Node{buf, slice}
+               args = append(args, slice)
                slice.SetEsc(ir.EscNone)
        }
 
@@ -515,7 +530,7 @@ func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node {
        r.Args = args
        r1 := typecheck.Expr(r)
        r1 = walkExpr(r1, init)
-       r1.SetType(n.Type())
+       r1.SetType(typ)
 
        return r1
 }
index e2aae748a099e81db6f9b309cbbedd73907b2319..98a7fd04114a30645c47ce1236062ed56485ec62 100644 (file)
@@ -55,6 +55,11 @@ var builtins = [...]struct {
        {"runtime.concatstring4", 1},
        {"runtime.concatstring5", 1},
        {"runtime.concatstrings", 1},
+       {"runtime.concatbyte2", 1},
+       {"runtime.concatbyte3", 1},
+       {"runtime.concatbyte4", 1},
+       {"runtime.concatbyte5", 1},
+       {"runtime.concatbytes", 1},
        {"runtime.cmpstring", 1},
        {"runtime.intstring", 1},
        {"runtime.slicebytetostring", 1},
index d45888b7a8efa27a83874c49d299b7b2a499aa66..3c34541ee809a916b25932df89d3a43b8743bf13 100644 (file)
@@ -72,6 +72,49 @@ func concatstring5(buf *tmpBuf, a0, a1, a2, a3, a4 string) string {
        return concatstrings(buf, []string{a0, a1, a2, a3, a4})
 }
 
+// concatbytes implements a Go string concatenation x+y+z+... returning a slice
+// of bytes.
+// The operands are passed in the slice a.
+func concatbytes(a []string) []byte {
+       l := 0
+       for _, x := range a {
+               n := len(x)
+               if l+n < l {
+                       throw("string concatenation too long")
+               }
+               l += n
+       }
+       if l == 0 {
+               // This is to match the return type of the non-optimized concatenation.
+               return []byte{}
+       }
+
+       b := rawbyteslice(l)
+       offset := 0
+       for _, x := range a {
+               copy(b[offset:], x)
+               offset += len(x)
+       }
+
+       return b
+}
+
+func concatbyte2(a0, a1 string) []byte {
+       return concatbytes([]string{a0, a1})
+}
+
+func concatbyte3(a0, a1, a2 string) []byte {
+       return concatbytes([]string{a0, a1, a2})
+}
+
+func concatbyte4(a0, a1, a2, a3 string) []byte {
+       return concatbytes([]string{a0, a1, a2, a3})
+}
+
+func concatbyte5(a0, a1, a2, a3, a4 string) []byte {
+       return concatbytes([]string{a0, a1, a2, a3, a4})
+}
+
 // slicebytetostring converts a byte slice to a string.
 // It is inserted by the compiler into generated code.
 // ptr is a pointer to the first element of the slice;
index f98c062d1bbd0eb4e4709e93f8ee4a4f0c354f96..64dcf4c8428d4c9d0c7c69b80136f2e1a6f1518b 100644 (file)
@@ -26,6 +26,11 @@ func ToByteSlice() []byte { // Issue #24698
        return []byte("foo")
 }
 
+func ConvertToByteSlice(a, b, c string) []byte {
+       // amd64:`.*runtime.concatbyte3`
+       return []byte(a + b + c)
+}
+
 // Loading from read-only symbols should get transformed into constants.
 func ConstantLoad() {
        // 12592 = 0x3130