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>
This commit is contained in:
Paschalis Tsilias 2023-09-13 12:44:17 +03:00 committed by Gopher Robot
parent 3da4281df1
commit fe69121bc5
8 changed files with 438 additions and 287 deletions

View File

@ -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)
}
}
}

View 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 concatstring5(*[32]byte, string, string, string, string, string) string
func concatstrings(*[32]byte, []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 cmpstring(string, string) int
func intstring(*[4]byte, int64) string func intstring(*[4]byte, int64) string
func slicebytetostring(buf *[32]byte, ptr *byte, n int) string func slicebytetostring(buf *[32]byte, ptr *byte, n int) string

View File

@ -76,168 +76,173 @@ var runtimeDecls = [...]struct {
{"concatstring4", funcTag, 36}, {"concatstring4", funcTag, 36},
{"concatstring5", funcTag, 37}, {"concatstring5", funcTag, 37},
{"concatstrings", funcTag, 39}, {"concatstrings", funcTag, 39},
{"cmpstring", funcTag, 40}, {"concatbyte2", funcTag, 41},
{"intstring", funcTag, 43}, {"concatbyte3", funcTag, 42},
{"slicebytetostring", funcTag, 44}, {"concatbyte4", funcTag, 43},
{"slicebytetostringtmp", funcTag, 45}, {"concatbyte5", funcTag, 44},
{"slicerunetostring", funcTag, 48}, {"concatbytes", funcTag, 45},
{"stringtoslicebyte", funcTag, 50}, {"cmpstring", funcTag, 46},
{"stringtoslicerune", funcTag, 53}, {"intstring", funcTag, 49},
{"slicecopy", funcTag, 54}, {"slicebytetostring", funcTag, 50},
{"decoderune", funcTag, 55}, {"slicebytetostringtmp", funcTag, 51},
{"countrunes", funcTag, 56}, {"slicerunetostring", funcTag, 54},
{"convT", funcTag, 57}, {"stringtoslicebyte", funcTag, 55},
{"convTnoptr", funcTag, 57}, {"stringtoslicerune", funcTag, 58},
{"convT16", funcTag, 59}, {"slicecopy", funcTag, 59},
{"convT32", funcTag, 61}, {"decoderune", funcTag, 60},
{"convT64", funcTag, 62}, {"countrunes", funcTag, 61},
{"convTstring", funcTag, 63}, {"convT", funcTag, 62},
{"convTslice", funcTag, 66}, {"convTnoptr", funcTag, 62},
{"assertE2I", funcTag, 67}, {"convT16", funcTag, 64},
{"assertE2I2", funcTag, 67}, {"convT32", funcTag, 66},
{"panicdottypeE", funcTag, 68}, {"convT64", funcTag, 67},
{"panicdottypeI", funcTag, 68}, {"convTstring", funcTag, 68},
{"panicnildottype", funcTag, 69}, {"convTslice", funcTag, 71},
{"typeAssert", funcTag, 67}, {"assertE2I", funcTag, 72},
{"interfaceSwitch", funcTag, 70}, {"assertE2I2", funcTag, 72},
{"ifaceeq", funcTag, 72}, {"panicdottypeE", funcTag, 73},
{"efaceeq", funcTag, 72}, {"panicdottypeI", funcTag, 73},
{"panicrangestate", funcTag, 73}, {"panicnildottype", funcTag, 74},
{"deferrangefunc", funcTag, 74}, {"typeAssert", funcTag, 72},
{"rand32", funcTag, 75}, {"interfaceSwitch", funcTag, 75},
{"makemap64", funcTag, 77}, {"ifaceeq", funcTag, 77},
{"makemap", funcTag, 78}, {"efaceeq", funcTag, 77},
{"makemap_small", funcTag, 79}, {"panicrangestate", funcTag, 78},
{"mapaccess1", funcTag, 80}, {"deferrangefunc", funcTag, 79},
{"mapaccess1_fast32", funcTag, 81}, {"rand32", funcTag, 80},
{"mapaccess1_fast64", funcTag, 82}, {"makemap64", funcTag, 82},
{"mapaccess1_faststr", funcTag, 83}, {"makemap", funcTag, 83},
{"mapaccess1_fat", funcTag, 84}, {"makemap_small", funcTag, 84},
{"mapaccess2", funcTag, 85}, {"mapaccess1", funcTag, 85},
{"mapaccess2_fast32", funcTag, 86}, {"mapaccess1_fast32", funcTag, 86},
{"mapaccess2_fast64", funcTag, 87}, {"mapaccess1_fast64", funcTag, 87},
{"mapaccess2_faststr", funcTag, 88}, {"mapaccess1_faststr", funcTag, 88},
{"mapaccess2_fat", funcTag, 89}, {"mapaccess1_fat", funcTag, 89},
{"mapassign", funcTag, 80}, {"mapaccess2", funcTag, 90},
{"mapassign_fast32", funcTag, 81}, {"mapaccess2_fast32", funcTag, 91},
{"mapassign_fast32ptr", funcTag, 90}, {"mapaccess2_fast64", funcTag, 92},
{"mapassign_fast64", funcTag, 82}, {"mapaccess2_faststr", funcTag, 93},
{"mapassign_fast64ptr", funcTag, 90}, {"mapaccess2_fat", funcTag, 94},
{"mapassign_faststr", funcTag, 83}, {"mapassign", funcTag, 85},
{"mapiterinit", funcTag, 91}, {"mapassign_fast32", funcTag, 86},
{"mapdelete", funcTag, 91}, {"mapassign_fast32ptr", funcTag, 95},
{"mapdelete_fast32", funcTag, 92}, {"mapassign_fast64", funcTag, 87},
{"mapdelete_fast64", funcTag, 93}, {"mapassign_fast64ptr", funcTag, 95},
{"mapdelete_faststr", funcTag, 94}, {"mapassign_faststr", funcTag, 88},
{"mapiternext", funcTag, 95}, {"mapiterinit", funcTag, 96},
{"mapclear", funcTag, 96}, {"mapdelete", funcTag, 96},
{"makechan64", funcTag, 98}, {"mapdelete_fast32", funcTag, 97},
{"makechan", funcTag, 99}, {"mapdelete_fast64", funcTag, 98},
{"chanrecv1", funcTag, 101}, {"mapdelete_faststr", funcTag, 99},
{"chanrecv2", funcTag, 102}, {"mapiternext", funcTag, 100},
{"chansend1", funcTag, 104}, {"mapclear", funcTag, 101},
{"closechan", funcTag, 105}, {"makechan64", funcTag, 103},
{"chanlen", funcTag, 106}, {"makechan", funcTag, 104},
{"chancap", funcTag, 106}, {"chanrecv1", funcTag, 106},
{"writeBarrier", varTag, 108}, {"chanrecv2", funcTag, 107},
{"typedmemmove", funcTag, 109}, {"chansend1", funcTag, 109},
{"typedmemclr", funcTag, 110}, {"closechan", funcTag, 110},
{"typedslicecopy", funcTag, 111}, {"chanlen", funcTag, 111},
{"selectnbsend", funcTag, 112}, {"chancap", funcTag, 111},
{"selectnbrecv", funcTag, 113}, {"writeBarrier", varTag, 113},
{"selectsetpc", funcTag, 114}, {"typedmemmove", funcTag, 114},
{"selectgo", funcTag, 115}, {"typedmemclr", funcTag, 115},
{"typedslicecopy", funcTag, 116},
{"selectnbsend", funcTag, 117},
{"selectnbrecv", funcTag, 118},
{"selectsetpc", funcTag, 119},
{"selectgo", funcTag, 120},
{"block", funcTag, 9}, {"block", funcTag, 9},
{"makeslice", funcTag, 116}, {"makeslice", funcTag, 121},
{"makeslice64", funcTag, 117}, {"makeslice64", funcTag, 122},
{"makeslicecopy", funcTag, 118}, {"makeslicecopy", funcTag, 123},
{"growslice", funcTag, 120}, {"growslice", funcTag, 125},
{"unsafeslicecheckptr", funcTag, 121}, {"unsafeslicecheckptr", funcTag, 126},
{"panicunsafeslicelen", funcTag, 9}, {"panicunsafeslicelen", funcTag, 9},
{"panicunsafeslicenilptr", funcTag, 9}, {"panicunsafeslicenilptr", funcTag, 9},
{"unsafestringcheckptr", funcTag, 122}, {"unsafestringcheckptr", funcTag, 127},
{"panicunsafestringlen", funcTag, 9}, {"panicunsafestringlen", funcTag, 9},
{"panicunsafestringnilptr", funcTag, 9}, {"panicunsafestringnilptr", funcTag, 9},
{"memmove", funcTag, 123}, {"memmove", funcTag, 128},
{"memclrNoHeapPointers", funcTag, 124}, {"memclrNoHeapPointers", funcTag, 129},
{"memclrHasPointers", funcTag, 124}, {"memclrHasPointers", funcTag, 129},
{"memequal", funcTag, 125}, {"memequal", funcTag, 130},
{"memequal0", funcTag, 126}, {"memequal0", funcTag, 131},
{"memequal8", funcTag, 126}, {"memequal8", funcTag, 131},
{"memequal16", funcTag, 126}, {"memequal16", funcTag, 131},
{"memequal32", funcTag, 126}, {"memequal32", funcTag, 131},
{"memequal64", funcTag, 126}, {"memequal64", funcTag, 131},
{"memequal128", funcTag, 126}, {"memequal128", funcTag, 131},
{"f32equal", funcTag, 127}, {"f32equal", funcTag, 132},
{"f64equal", funcTag, 127}, {"f64equal", funcTag, 132},
{"c64equal", funcTag, 127}, {"c64equal", funcTag, 132},
{"c128equal", funcTag, 127}, {"c128equal", funcTag, 132},
{"strequal", funcTag, 127}, {"strequal", funcTag, 132},
{"interequal", funcTag, 127}, {"interequal", funcTag, 132},
{"nilinterequal", funcTag, 127}, {"nilinterequal", funcTag, 132},
{"memhash", funcTag, 128}, {"memhash", funcTag, 133},
{"memhash0", funcTag, 129}, {"memhash0", funcTag, 134},
{"memhash8", funcTag, 129}, {"memhash8", funcTag, 134},
{"memhash16", funcTag, 129}, {"memhash16", funcTag, 134},
{"memhash32", funcTag, 129}, {"memhash32", funcTag, 134},
{"memhash64", funcTag, 129}, {"memhash64", funcTag, 134},
{"memhash128", funcTag, 129}, {"memhash128", funcTag, 134},
{"f32hash", funcTag, 130}, {"f32hash", funcTag, 135},
{"f64hash", funcTag, 130}, {"f64hash", funcTag, 135},
{"c64hash", funcTag, 130}, {"c64hash", funcTag, 135},
{"c128hash", funcTag, 130}, {"c128hash", funcTag, 135},
{"strhash", funcTag, 130}, {"strhash", funcTag, 135},
{"interhash", funcTag, 130}, {"interhash", funcTag, 135},
{"nilinterhash", funcTag, 130}, {"nilinterhash", funcTag, 135},
{"int64div", funcTag, 131}, {"int64div", funcTag, 136},
{"uint64div", funcTag, 132}, {"uint64div", funcTag, 137},
{"int64mod", funcTag, 131}, {"int64mod", funcTag, 136},
{"uint64mod", funcTag, 132}, {"uint64mod", funcTag, 137},
{"float64toint64", funcTag, 133}, {"float64toint64", funcTag, 138},
{"float64touint64", funcTag, 134}, {"float64touint64", funcTag, 139},
{"float64touint32", funcTag, 135}, {"float64touint32", funcTag, 140},
{"int64tofloat64", funcTag, 136}, {"int64tofloat64", funcTag, 141},
{"int64tofloat32", funcTag, 138}, {"int64tofloat32", funcTag, 143},
{"uint64tofloat64", funcTag, 139}, {"uint64tofloat64", funcTag, 144},
{"uint64tofloat32", funcTag, 140}, {"uint64tofloat32", funcTag, 145},
{"uint32tofloat64", funcTag, 141}, {"uint32tofloat64", funcTag, 146},
{"complex128div", funcTag, 142}, {"complex128div", funcTag, 147},
{"getcallerpc", funcTag, 143}, {"getcallerpc", funcTag, 148},
{"getcallersp", funcTag, 143}, {"getcallersp", funcTag, 148},
{"racefuncenter", funcTag, 31}, {"racefuncenter", funcTag, 31},
{"racefuncexit", funcTag, 9}, {"racefuncexit", funcTag, 9},
{"raceread", funcTag, 31}, {"raceread", funcTag, 31},
{"racewrite", funcTag, 31}, {"racewrite", funcTag, 31},
{"racereadrange", funcTag, 144}, {"racereadrange", funcTag, 149},
{"racewriterange", funcTag, 144}, {"racewriterange", funcTag, 149},
{"msanread", funcTag, 144}, {"msanread", funcTag, 149},
{"msanwrite", funcTag, 144}, {"msanwrite", funcTag, 149},
{"msanmove", funcTag, 145}, {"msanmove", funcTag, 150},
{"asanread", funcTag, 144}, {"asanread", funcTag, 149},
{"asanwrite", funcTag, 144}, {"asanwrite", funcTag, 149},
{"checkptrAlignment", funcTag, 146}, {"checkptrAlignment", funcTag, 151},
{"checkptrArithmetic", funcTag, 148}, {"checkptrArithmetic", funcTag, 153},
{"libfuzzerTraceCmp1", funcTag, 149}, {"libfuzzerTraceCmp1", funcTag, 154},
{"libfuzzerTraceCmp2", funcTag, 150}, {"libfuzzerTraceCmp2", funcTag, 155},
{"libfuzzerTraceCmp4", funcTag, 151}, {"libfuzzerTraceCmp4", funcTag, 156},
{"libfuzzerTraceCmp8", funcTag, 152}, {"libfuzzerTraceCmp8", funcTag, 157},
{"libfuzzerTraceConstCmp1", funcTag, 149}, {"libfuzzerTraceConstCmp1", funcTag, 154},
{"libfuzzerTraceConstCmp2", funcTag, 150}, {"libfuzzerTraceConstCmp2", funcTag, 155},
{"libfuzzerTraceConstCmp4", funcTag, 151}, {"libfuzzerTraceConstCmp4", funcTag, 156},
{"libfuzzerTraceConstCmp8", funcTag, 152}, {"libfuzzerTraceConstCmp8", funcTag, 157},
{"libfuzzerHookStrCmp", funcTag, 153}, {"libfuzzerHookStrCmp", funcTag, 158},
{"libfuzzerHookEqualFold", funcTag, 153}, {"libfuzzerHookEqualFold", funcTag, 158},
{"addCovMeta", funcTag, 155}, {"addCovMeta", funcTag, 160},
{"x86HasPOPCNT", varTag, 6}, {"x86HasPOPCNT", varTag, 6},
{"x86HasSSE41", varTag, 6}, {"x86HasSSE41", varTag, 6},
{"x86HasFMA", varTag, 6}, {"x86HasFMA", varTag, 6},
{"armHasVFPv4", varTag, 6}, {"armHasVFPv4", varTag, 6},
{"arm64HasATOMICS", varTag, 6}, {"arm64HasATOMICS", varTag, 6},
{"asanregisterglobals", funcTag, 124}, {"asanregisterglobals", funcTag, 129},
} }
func runtimeTypes() []*types.Type { func runtimeTypes() []*types.Type {
var typs [156]*types.Type var typs [161]*types.Type
typs[0] = types.ByteType typs[0] = types.ByteType
typs[1] = types.NewPtr(typs[0]) typs[1] = types.NewPtr(typs[0])
typs[2] = types.Types[types.TANY] 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[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[38] = types.NewSlice(typs[28])
typs[39] = newSig(params(typs[33], typs[38]), params(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[40] = types.NewSlice(typs[0])
typs[41] = types.NewArray(typs[0], 4) typs[41] = newSig(params(typs[28], typs[28]), params(typs[40]))
typs[42] = types.NewPtr(typs[41]) typs[42] = newSig(params(typs[28], typs[28], typs[28]), params(typs[40]))
typs[43] = newSig(params(typs[42], typs[22]), params(typs[28])) typs[43] = newSig(params(typs[28], typs[28], typs[28], typs[28]), params(typs[40]))
typs[44] = newSig(params(typs[33], typs[1], typs[15]), params(typs[28])) typs[44] = newSig(params(typs[28], typs[28], typs[28], typs[28], typs[28]), params(typs[40]))
typs[45] = newSig(params(typs[1], typs[15]), params(typs[28])) typs[45] = newSig(params(typs[38]), params(typs[40]))
typs[46] = types.RuneType typs[46] = newSig(params(typs[28], typs[28]), params(typs[15]))
typs[47] = types.NewSlice(typs[46]) typs[47] = types.NewArray(typs[0], 4)
typs[48] = newSig(params(typs[33], typs[47]), params(typs[28])) typs[48] = types.NewPtr(typs[47])
typs[49] = types.NewSlice(typs[0]) typs[49] = newSig(params(typs[48], typs[22]), params(typs[28]))
typs[50] = newSig(params(typs[33], typs[28]), params(typs[49])) typs[50] = newSig(params(typs[33], typs[1], typs[15]), params(typs[28]))
typs[51] = types.NewArray(typs[46], 32) typs[51] = newSig(params(typs[1], typs[15]), params(typs[28]))
typs[52] = types.NewPtr(typs[51]) typs[52] = types.RuneType
typs[53] = newSig(params(typs[52], typs[28]), params(typs[47])) typs[53] = types.NewSlice(typs[52])
typs[54] = newSig(params(typs[3], typs[15], typs[3], typs[15], typs[5]), params(typs[15])) typs[54] = newSig(params(typs[33], typs[53]), params(typs[28]))
typs[55] = newSig(params(typs[28], typs[15]), params(typs[46], typs[15])) typs[55] = newSig(params(typs[33], typs[28]), params(typs[40]))
typs[56] = newSig(params(typs[28]), params(typs[15])) typs[56] = types.NewArray(typs[52], 32)
typs[57] = newSig(params(typs[1], typs[3]), params(typs[7])) typs[57] = types.NewPtr(typs[56])
typs[58] = types.Types[types.TUINT16] typs[58] = newSig(params(typs[57], typs[28]), params(typs[53]))
typs[59] = newSig(params(typs[58]), params(typs[7])) typs[59] = newSig(params(typs[3], typs[15], typs[3], typs[15], typs[5]), params(typs[15]))
typs[60] = types.Types[types.TUINT32] typs[60] = newSig(params(typs[28], typs[15]), params(typs[52], typs[15]))
typs[61] = newSig(params(typs[60]), params(typs[7])) typs[61] = newSig(params(typs[28]), params(typs[15]))
typs[62] = newSig(params(typs[24]), params(typs[7])) typs[62] = newSig(params(typs[1], typs[3]), params(typs[7]))
typs[63] = newSig(params(typs[28]), params(typs[7])) typs[63] = types.Types[types.TUINT16]
typs[64] = types.Types[types.TUINT8] typs[64] = newSig(params(typs[63]), params(typs[7]))
typs[65] = types.NewSlice(typs[64]) typs[65] = types.Types[types.TUINT32]
typs[66] = newSig(params(typs[65]), params(typs[7])) typs[66] = newSig(params(typs[65]), params(typs[7]))
typs[67] = newSig(params(typs[1], typs[1]), params(typs[1])) typs[67] = newSig(params(typs[24]), params(typs[7]))
typs[68] = newSig(params(typs[1], typs[1], typs[1]), nil) typs[68] = newSig(params(typs[28]), params(typs[7]))
typs[69] = newSig(params(typs[1]), nil) typs[69] = types.Types[types.TUINT8]
typs[70] = newSig(params(typs[1], typs[1]), params(typs[15], typs[1])) typs[70] = types.NewSlice(typs[69])
typs[71] = types.NewPtr(typs[5]) typs[71] = newSig(params(typs[70]), params(typs[7]))
typs[72] = newSig(params(typs[71], typs[7], typs[7]), params(typs[6])) typs[72] = newSig(params(typs[1], typs[1]), params(typs[1]))
typs[73] = newSig(params(typs[15]), nil) typs[73] = newSig(params(typs[1], typs[1], typs[1]), nil)
typs[74] = newSig(nil, params(typs[10])) typs[74] = newSig(params(typs[1]), nil)
typs[75] = newSig(nil, params(typs[60])) typs[75] = newSig(params(typs[1], typs[1]), params(typs[15], typs[1]))
typs[76] = types.NewMap(typs[2], typs[2]) typs[76] = types.NewPtr(typs[5])
typs[77] = newSig(params(typs[1], typs[22], typs[3]), params(typs[76])) typs[77] = newSig(params(typs[76], typs[7], typs[7]), params(typs[6]))
typs[78] = newSig(params(typs[1], typs[15], typs[3]), params(typs[76])) typs[78] = newSig(params(typs[15]), nil)
typs[79] = newSig(nil, params(typs[76])) typs[79] = newSig(nil, params(typs[10]))
typs[80] = newSig(params(typs[1], typs[76], typs[3]), params(typs[3])) typs[80] = newSig(nil, params(typs[65]))
typs[81] = newSig(params(typs[1], typs[76], typs[60]), params(typs[3])) typs[81] = types.NewMap(typs[2], typs[2])
typs[82] = newSig(params(typs[1], typs[76], typs[24]), params(typs[3])) typs[82] = newSig(params(typs[1], typs[22], typs[3]), params(typs[81]))
typs[83] = newSig(params(typs[1], typs[76], typs[28]), params(typs[3])) typs[83] = newSig(params(typs[1], typs[15], typs[3]), params(typs[81]))
typs[84] = newSig(params(typs[1], typs[76], typs[3], typs[1]), params(typs[3])) typs[84] = newSig(nil, params(typs[81]))
typs[85] = newSig(params(typs[1], typs[76], typs[3]), params(typs[3], typs[6])) typs[85] = newSig(params(typs[1], typs[81], typs[3]), params(typs[3]))
typs[86] = newSig(params(typs[1], typs[76], typs[60]), params(typs[3], typs[6])) typs[86] = newSig(params(typs[1], typs[81], typs[65]), params(typs[3]))
typs[87] = newSig(params(typs[1], typs[76], typs[24]), params(typs[3], typs[6])) typs[87] = newSig(params(typs[1], typs[81], typs[24]), params(typs[3]))
typs[88] = newSig(params(typs[1], typs[76], typs[28]), params(typs[3], typs[6])) typs[88] = newSig(params(typs[1], typs[81], typs[28]), params(typs[3]))
typs[89] = newSig(params(typs[1], typs[76], typs[3], typs[1]), params(typs[3], typs[6])) typs[89] = newSig(params(typs[1], typs[81], typs[3], typs[1]), params(typs[3]))
typs[90] = newSig(params(typs[1], typs[76], typs[7]), 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[76], typs[3]), nil) typs[91] = newSig(params(typs[1], typs[81], typs[65]), params(typs[3], typs[6]))
typs[92] = newSig(params(typs[1], typs[76], typs[60]), nil) typs[92] = newSig(params(typs[1], typs[81], typs[24]), params(typs[3], typs[6]))
typs[93] = newSig(params(typs[1], typs[76], typs[24]), nil) typs[93] = newSig(params(typs[1], typs[81], typs[28]), params(typs[3], typs[6]))
typs[94] = newSig(params(typs[1], typs[76], typs[28]), nil) typs[94] = newSig(params(typs[1], typs[81], typs[3], typs[1]), params(typs[3], typs[6]))
typs[95] = newSig(params(typs[3]), nil) typs[95] = newSig(params(typs[1], typs[81], typs[7]), params(typs[3]))
typs[96] = newSig(params(typs[1], typs[76]), nil) typs[96] = newSig(params(typs[1], typs[81], typs[3]), nil)
typs[97] = types.NewChan(typs[2], types.Cboth) typs[97] = newSig(params(typs[1], typs[81], typs[65]), nil)
typs[98] = newSig(params(typs[1], typs[22]), params(typs[97])) typs[98] = newSig(params(typs[1], typs[81], typs[24]), nil)
typs[99] = newSig(params(typs[1], typs[15]), params(typs[97])) typs[99] = newSig(params(typs[1], typs[81], typs[28]), nil)
typs[100] = types.NewChan(typs[2], types.Crecv) typs[100] = newSig(params(typs[3]), nil)
typs[101] = newSig(params(typs[100], typs[3]), nil) typs[101] = newSig(params(typs[1], typs[81]), nil)
typs[102] = newSig(params(typs[100], typs[3]), params(typs[6])) typs[102] = types.NewChan(typs[2], types.Cboth)
typs[103] = types.NewChan(typs[2], types.Csend) typs[103] = newSig(params(typs[1], typs[22]), params(typs[102]))
typs[104] = newSig(params(typs[103], typs[3]), nil) typs[104] = newSig(params(typs[1], typs[15]), params(typs[102]))
typs[105] = newSig(params(typs[103]), nil) typs[105] = types.NewChan(typs[2], types.Crecv)
typs[106] = newSig(params(typs[2]), params(typs[15])) typs[106] = newSig(params(typs[105], typs[3]), nil)
typs[107] = types.NewArray(typs[0], 3) typs[107] = newSig(params(typs[105], typs[3]), params(typs[6]))
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[108] = types.NewChan(typs[2], types.Csend)
typs[109] = newSig(params(typs[1], typs[3], typs[3]), nil) typs[109] = newSig(params(typs[108], typs[3]), nil)
typs[110] = newSig(params(typs[1], typs[3]), nil) typs[110] = newSig(params(typs[108]), nil)
typs[111] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15])) typs[111] = newSig(params(typs[2]), params(typs[15]))
typs[112] = newSig(params(typs[103], typs[3]), params(typs[6])) typs[112] = types.NewArray(typs[0], 3)
typs[113] = newSig(params(typs[3], typs[100]), params(typs[6], typs[6])) 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[71]), nil) typs[114] = newSig(params(typs[1], typs[3], typs[3]), nil)
typs[115] = newSig(params(typs[1], typs[1], typs[71], typs[15], typs[15], typs[6]), params(typs[15], typs[6])) typs[115] = newSig(params(typs[1], typs[3]), nil)
typs[116] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7])) typs[116] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
typs[117] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7])) typs[117] = newSig(params(typs[108], typs[3]), params(typs[6]))
typs[118] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7])) typs[118] = newSig(params(typs[3], typs[105]), params(typs[6], typs[6]))
typs[119] = types.NewSlice(typs[2]) typs[119] = newSig(params(typs[76]), nil)
typs[120] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[119])) 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[7], typs[22]), nil) typs[121] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
typs[122] = newSig(params(typs[7], typs[22]), nil) typs[122] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
typs[123] = newSig(params(typs[3], typs[3], typs[5]), nil) typs[123] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
typs[124] = newSig(params(typs[7], typs[5]), nil) typs[124] = types.NewSlice(typs[2])
typs[125] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6])) typs[125] = newSig(params(typs[3], typs[15], typs[15], typs[15], typs[1]), params(typs[124]))
typs[126] = newSig(params(typs[3], typs[3]), params(typs[6])) typs[126] = newSig(params(typs[1], typs[7], typs[22]), nil)
typs[127] = newSig(params(typs[7], typs[7]), params(typs[6])) typs[127] = newSig(params(typs[7], typs[22]), nil)
typs[128] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5])) typs[128] = newSig(params(typs[3], typs[3], typs[5]), nil)
typs[129] = newSig(params(typs[7], typs[5]), params(typs[5])) typs[129] = newSig(params(typs[7], typs[5]), nil)
typs[130] = newSig(params(typs[3], typs[5]), params(typs[5])) typs[130] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
typs[131] = newSig(params(typs[22], typs[22]), params(typs[22])) typs[131] = newSig(params(typs[3], typs[3]), params(typs[6]))
typs[132] = newSig(params(typs[24], typs[24]), params(typs[24])) typs[132] = newSig(params(typs[7], typs[7]), params(typs[6]))
typs[133] = newSig(params(typs[20]), params(typs[22])) typs[133] = newSig(params(typs[3], typs[5], typs[5]), params(typs[5]))
typs[134] = newSig(params(typs[20]), params(typs[24])) typs[134] = newSig(params(typs[7], typs[5]), params(typs[5]))
typs[135] = newSig(params(typs[20]), params(typs[60])) typs[135] = newSig(params(typs[3], typs[5]), params(typs[5]))
typs[136] = newSig(params(typs[22]), params(typs[20])) typs[136] = newSig(params(typs[22], typs[22]), params(typs[22]))
typs[137] = types.Types[types.TFLOAT32] typs[137] = newSig(params(typs[24], typs[24]), params(typs[24]))
typs[138] = newSig(params(typs[22]), params(typs[137])) typs[138] = newSig(params(typs[20]), params(typs[22]))
typs[139] = newSig(params(typs[24]), params(typs[20])) typs[139] = newSig(params(typs[20]), params(typs[24]))
typs[140] = newSig(params(typs[24]), params(typs[137])) typs[140] = newSig(params(typs[20]), params(typs[65]))
typs[141] = newSig(params(typs[60]), params(typs[20])) typs[141] = newSig(params(typs[22]), params(typs[20]))
typs[142] = newSig(params(typs[26], typs[26]), params(typs[26])) typs[142] = types.Types[types.TFLOAT32]
typs[143] = newSig(nil, params(typs[5])) typs[143] = newSig(params(typs[22]), params(typs[142]))
typs[144] = newSig(params(typs[5], typs[5]), nil) typs[144] = newSig(params(typs[24]), params(typs[20]))
typs[145] = newSig(params(typs[5], typs[5], typs[5]), nil) typs[145] = newSig(params(typs[24]), params(typs[142]))
typs[146] = newSig(params(typs[7], typs[1], typs[5]), nil) typs[146] = newSig(params(typs[65]), params(typs[20]))
typs[147] = types.NewSlice(typs[7]) typs[147] = newSig(params(typs[26], typs[26]), params(typs[26]))
typs[148] = newSig(params(typs[7], typs[147]), nil) typs[148] = newSig(nil, params(typs[5]))
typs[149] = newSig(params(typs[64], typs[64], typs[17]), nil) typs[149] = newSig(params(typs[5], typs[5]), nil)
typs[150] = newSig(params(typs[58], typs[58], typs[17]), nil) typs[150] = newSig(params(typs[5], typs[5], typs[5]), nil)
typs[151] = newSig(params(typs[60], typs[60], typs[17]), nil) typs[151] = newSig(params(typs[7], typs[1], typs[5]), nil)
typs[152] = newSig(params(typs[24], typs[24], typs[17]), nil) typs[152] = types.NewSlice(typs[7])
typs[153] = newSig(params(typs[28], typs[28], typs[17]), nil) typs[153] = newSig(params(typs[7], typs[152]), nil)
typs[154] = types.NewArray(typs[0], 16) typs[154] = newSig(params(typs[69], typs[69], typs[17]), nil)
typs[155] = newSig(params(typs[7], typs[60], typs[154], typs[28], typs[15], typs[64], typs[64]), params(typs[60])) 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[:] return typs[:]
} }

View File

@ -270,6 +270,11 @@ func walkRuneToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
// walkStringToBytes walks an OSTR2BYTES node. // walkStringToBytes walks an OSTR2BYTES node.
func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node { func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
s := n.X s := n.X
if expr, ok := s.(*ir.AddStringExpr); ok {
return walkAddString(n.Type(), expr, init)
}
if ir.IsConst(s, constant.String) { if ir.IsConst(s, constant.String) {
sc := ir.StringVal(s) sc := ir.StringVal(s)

View File

@ -273,7 +273,7 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
return walkNew(n, init) return walkNew(n, init)
case ir.OADDSTR: case ir.OADDSTR:
return walkAddString(n.(*ir.AddStringExpr), init) return walkAddString(n.Type(), n.(*ir.AddStringExpr), init)
case ir.OAPPEND: case ir.OAPPEND:
// order should make sure we only see OAS(node, OAPPEND), which we handle above. // 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 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) c := len(n.List)
if c < 2 { if c < 2 {
base.Fatalf("walkAddString count %d too small", c) base.Fatalf("walkAddString count %d too small", c)
} }
buf := typecheck.NodNil() // list of string arguments
if n.Esc() == ir.EscNone { var args []ir.Node
sz := int64(0)
for _, n1 := range n.List { var fn, fnsmall, fnbig string
if n1.Op() == ir.OLITERAL {
sz += int64(len(ir.StringVal(n1))) 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. args = []ir.Node{buf}
if sz < tmpstringbufsize { fnsmall, fnbig = "concatstring%d", "concatstrings"
// Create temporary buffer for result string on stack. case typ.IsSlice() && typ.Elem().IsKind(types.TUINT8): // Optimize []byte(str1+str2+...)
buf = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8]) fnsmall, fnbig = "concatbyte%d", "concatbytes"
}
} }
// build list of string arguments
args := []ir.Node{buf}
for _, n2 := range n.List {
args = append(args, typecheck.Conv(n2, types.Types[types.TSTRING]))
}
var fn string
if c <= 5 { if c <= 5 {
// small numbers of strings use direct runtime helpers. // small numbers of strings use direct runtime helpers.
// note: order.expr knows this cutoff too. // 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 { } else {
// large numbers of strings are passed to the runtime as a slice. // large numbers of strings are passed to the runtime as a slice.
fn = "concatstrings" fn = fnbig
t := types.NewSlice(types.Types[types.TSTRING]) 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 slice.Prealloc = n.Prealloc
args = []ir.Node{buf, slice} args = append(args, slice)
slice.SetEsc(ir.EscNone) slice.SetEsc(ir.EscNone)
} }
@ -515,7 +530,7 @@ func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node {
r.Args = args r.Args = args
r1 := typecheck.Expr(r) r1 := typecheck.Expr(r)
r1 = walkExpr(r1, init) r1 = walkExpr(r1, init)
r1.SetType(n.Type()) r1.SetType(typ)
return r1 return r1
} }

View File

@ -55,6 +55,11 @@ var builtins = [...]struct {
{"runtime.concatstring4", 1}, {"runtime.concatstring4", 1},
{"runtime.concatstring5", 1}, {"runtime.concatstring5", 1},
{"runtime.concatstrings", 1}, {"runtime.concatstrings", 1},
{"runtime.concatbyte2", 1},
{"runtime.concatbyte3", 1},
{"runtime.concatbyte4", 1},
{"runtime.concatbyte5", 1},
{"runtime.concatbytes", 1},
{"runtime.cmpstring", 1}, {"runtime.cmpstring", 1},
{"runtime.intstring", 1}, {"runtime.intstring", 1},
{"runtime.slicebytetostring", 1}, {"runtime.slicebytetostring", 1},

View 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}) 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. // slicebytetostring converts a byte slice to a string.
// It is inserted by the compiler into generated code. // It is inserted by the compiler into generated code.
// ptr is a pointer to the first element of the slice; // ptr is a pointer to the first element of the slice;

View File

@ -26,6 +26,11 @@ func ToByteSlice() []byte { // Issue #24698
return []byte("foo") 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. // Loading from read-only symbols should get transformed into constants.
func ConstantLoad() { func ConstantLoad() {
// 12592 = 0x3130 // 12592 = 0x3130