aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaschalis Tsilias <paschalis.tsilias@gmail.com>2023-09-13 12:44:17 +0300
committerGopher Robot <gobot@golang.org>2024-09-10 21:20:57 +0000
commitfe69121bc538260cf91f11dab705335b690e51a3 (patch)
treed5962a3404521bd5bed263fad4156d462f6673c6 /src
parent3da4281df1b0c7ea11b524ff19fc2f409b8e58c0 (diff)
downloadgo-fe69121bc538260cf91f11dab705335b690e51a3.tar.xz
cmd/compile: optimize []byte(string1 + string2)
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>
Diffstat (limited to 'src')
-rw-r--r--src/cmd/compile/internal/test/issue62407_test.go62
-rw-r--r--src/cmd/compile/internal/typecheck/_builtin/runtime.go6
-rw-r--r--src/cmd/compile/internal/typecheck/builtin.go530
-rw-r--r--src/cmd/compile/internal/walk/convert.go5
-rw-r--r--src/cmd/compile/internal/walk/expr.go67
-rw-r--r--src/cmd/internal/goobj/builtinlist.go5
-rw-r--r--src/runtime/string.go43
7 files changed, 432 insertions, 286 deletions
diff --git a/src/cmd/compile/internal/test/issue62407_test.go b/src/cmd/compile/internal/test/issue62407_test.go
new file mode 100644
index 0000000000..d065673627
--- /dev/null
+++ b/src/cmd/compile/internal/test/issue62407_test.go
@@ -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)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/typecheck/_builtin/runtime.go b/src/cmd/compile/internal/typecheck/_builtin/runtime.go
index 3a5d3576be..bf6b586725 100644
--- a/src/cmd/compile/internal/typecheck/_builtin/runtime.go
+++ b/src/cmd/compile/internal/typecheck/_builtin/runtime.go
@@ -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
diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go
index 2f43b1d01c..b4d2ff73ec 100644
--- a/src/cmd/compile/internal/typecheck/builtin.go
+++ b/src/cmd/compile/internal/typecheck/builtin.go
@@ -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[:]
}
diff --git a/src/cmd/compile/internal/walk/convert.go b/src/cmd/compile/internal/walk/convert.go
index 280b3b65e8..3118233697 100644
--- a/src/cmd/compile/internal/walk/convert.go
+++ b/src/cmd/compile/internal/walk/convert.go
@@ -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)
diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go
index f73b5d9503..8c36e03aa0 100644
--- a/src/cmd/compile/internal/walk/expr.go
+++ b/src/cmd/compile/internal/walk/expr.go
@@ -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
}
diff --git a/src/cmd/internal/goobj/builtinlist.go b/src/cmd/internal/goobj/builtinlist.go
index e2aae748a0..98a7fd0411 100644
--- a/src/cmd/internal/goobj/builtinlist.go
+++ b/src/cmd/internal/goobj/builtinlist.go
@@ -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},
diff --git a/src/runtime/string.go b/src/runtime/string.go
index d45888b7a8..3c34541ee8 100644
--- a/src/runtime/string.go
+++ b/src/runtime/string.go
@@ -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;