cmd/compile: optimize len([]rune(string))

Adds a new runtime function to count runes in a string.
Modifies the compiler to detect the pattern len([]rune(string))
and replaces it with the new rune counting runtime function.

RuneCount/lenruneslice/ASCII                  27.8ns ± 2%  14.5ns ± 3%  -47.70%  (p=0.000 n=10+10)
RuneCount/lenruneslice/Japanese                126ns ± 2%    60ns ± 2%  -52.03%  (p=0.000 n=10+10)
RuneCount/lenruneslice/MixedLength             104ns ± 2%    50ns ± 1%  -51.71%  (p=0.000 n=10+9)

Fixes #24923

Change-Id: Ie9c7e7391a4e2cca675c5cdcc1e5ce7d523948b9
Reviewed-on: https://go-review.googlesource.com/108985
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
This commit is contained in:
Martin Möhrmann 2018-04-24 15:13:08 +02:00
parent a8a60ac2a7
commit b9a59d9f2e
8 changed files with 265 additions and 169 deletions

View file

@ -46,112 +46,113 @@ var runtimeDecls = [...]struct {
{"slicerunetostring", funcTag, 42},
{"stringtoslicebyte", funcTag, 43},
{"stringtoslicerune", funcTag, 46},
{"decoderune", funcTag, 47},
{"slicecopy", funcTag, 49},
{"slicestringcopy", funcTag, 50},
{"convI2I", funcTag, 51},
{"convT2E", funcTag, 52},
{"convT2E16", funcTag, 51},
{"convT2E32", funcTag, 51},
{"convT2E64", funcTag, 51},
{"convT2Estring", funcTag, 52},
{"convT2Eslice", funcTag, 52},
{"convT2Enoptr", funcTag, 52},
{"convT2I", funcTag, 52},
{"convT2I16", funcTag, 51},
{"convT2I32", funcTag, 51},
{"convT2I64", funcTag, 51},
{"convT2Istring", funcTag, 52},
{"convT2Islice", funcTag, 52},
{"convT2Inoptr", funcTag, 52},
{"assertE2I", funcTag, 51},
{"assertE2I2", funcTag, 53},
{"assertI2I", funcTag, 51},
{"assertI2I2", funcTag, 53},
{"panicdottypeE", funcTag, 54},
{"panicdottypeI", funcTag, 54},
{"panicnildottype", funcTag, 55},
{"ifaceeq", funcTag, 58},
{"efaceeq", funcTag, 58},
{"fastrand", funcTag, 60},
{"makemap64", funcTag, 62},
{"makemap", funcTag, 63},
{"makemap_small", funcTag, 64},
{"mapaccess1", funcTag, 65},
{"mapaccess1_fast32", funcTag, 66},
{"mapaccess1_fast64", funcTag, 66},
{"mapaccess1_faststr", funcTag, 66},
{"mapaccess1_fat", funcTag, 67},
{"mapaccess2", funcTag, 68},
{"mapaccess2_fast32", funcTag, 69},
{"mapaccess2_fast64", funcTag, 69},
{"mapaccess2_faststr", funcTag, 69},
{"mapaccess2_fat", funcTag, 70},
{"mapassign", funcTag, 65},
{"mapassign_fast32", funcTag, 66},
{"mapassign_fast32ptr", funcTag, 66},
{"mapassign_fast64", funcTag, 66},
{"mapassign_fast64ptr", funcTag, 66},
{"mapassign_faststr", funcTag, 66},
{"mapiterinit", funcTag, 71},
{"mapdelete", funcTag, 71},
{"mapdelete_fast32", funcTag, 72},
{"mapdelete_fast64", funcTag, 72},
{"mapdelete_faststr", funcTag, 72},
{"mapiternext", funcTag, 73},
{"makechan64", funcTag, 75},
{"makechan", funcTag, 76},
{"chanrecv1", funcTag, 78},
{"chanrecv2", funcTag, 79},
{"chansend1", funcTag, 81},
{"slicecopy", funcTag, 48},
{"slicestringcopy", funcTag, 49},
{"decoderune", funcTag, 50},
{"countrunes", funcTag, 51},
{"convI2I", funcTag, 52},
{"convT2E", funcTag, 53},
{"convT2E16", funcTag, 52},
{"convT2E32", funcTag, 52},
{"convT2E64", funcTag, 52},
{"convT2Estring", funcTag, 53},
{"convT2Eslice", funcTag, 53},
{"convT2Enoptr", funcTag, 53},
{"convT2I", funcTag, 53},
{"convT2I16", funcTag, 52},
{"convT2I32", funcTag, 52},
{"convT2I64", funcTag, 52},
{"convT2Istring", funcTag, 53},
{"convT2Islice", funcTag, 53},
{"convT2Inoptr", funcTag, 53},
{"assertE2I", funcTag, 52},
{"assertE2I2", funcTag, 54},
{"assertI2I", funcTag, 52},
{"assertI2I2", funcTag, 54},
{"panicdottypeE", funcTag, 55},
{"panicdottypeI", funcTag, 55},
{"panicnildottype", funcTag, 56},
{"ifaceeq", funcTag, 59},
{"efaceeq", funcTag, 59},
{"fastrand", funcTag, 61},
{"makemap64", funcTag, 63},
{"makemap", funcTag, 64},
{"makemap_small", funcTag, 65},
{"mapaccess1", funcTag, 66},
{"mapaccess1_fast32", funcTag, 67},
{"mapaccess1_fast64", funcTag, 67},
{"mapaccess1_faststr", funcTag, 67},
{"mapaccess1_fat", funcTag, 68},
{"mapaccess2", funcTag, 69},
{"mapaccess2_fast32", funcTag, 70},
{"mapaccess2_fast64", funcTag, 70},
{"mapaccess2_faststr", funcTag, 70},
{"mapaccess2_fat", funcTag, 71},
{"mapassign", funcTag, 66},
{"mapassign_fast32", funcTag, 67},
{"mapassign_fast32ptr", funcTag, 67},
{"mapassign_fast64", funcTag, 67},
{"mapassign_fast64ptr", funcTag, 67},
{"mapassign_faststr", funcTag, 67},
{"mapiterinit", funcTag, 72},
{"mapdelete", funcTag, 72},
{"mapdelete_fast32", funcTag, 73},
{"mapdelete_fast64", funcTag, 73},
{"mapdelete_faststr", funcTag, 73},
{"mapiternext", funcTag, 74},
{"makechan64", funcTag, 76},
{"makechan", funcTag, 77},
{"chanrecv1", funcTag, 79},
{"chanrecv2", funcTag, 80},
{"chansend1", funcTag, 82},
{"closechan", funcTag, 23},
{"writeBarrier", varTag, 83},
{"typedmemmove", funcTag, 84},
{"typedmemclr", funcTag, 85},
{"typedslicecopy", funcTag, 86},
{"selectnbsend", funcTag, 87},
{"selectnbrecv", funcTag, 88},
{"selectnbrecv2", funcTag, 90},
{"selectsetpc", funcTag, 55},
{"selectgo", funcTag, 91},
{"writeBarrier", varTag, 84},
{"typedmemmove", funcTag, 85},
{"typedmemclr", funcTag, 86},
{"typedslicecopy", funcTag, 87},
{"selectnbsend", funcTag, 88},
{"selectnbrecv", funcTag, 89},
{"selectnbrecv2", funcTag, 91},
{"selectsetpc", funcTag, 56},
{"selectgo", funcTag, 92},
{"block", funcTag, 5},
{"makeslice", funcTag, 93},
{"makeslice64", funcTag, 94},
{"growslice", funcTag, 95},
{"memmove", funcTag, 96},
{"memclrNoHeapPointers", funcTag, 97},
{"memclrHasPointers", funcTag, 97},
{"memequal", funcTag, 98},
{"memequal8", funcTag, 99},
{"memequal16", funcTag, 99},
{"memequal32", funcTag, 99},
{"memequal64", funcTag, 99},
{"memequal128", funcTag, 99},
{"int64div", funcTag, 100},
{"uint64div", funcTag, 101},
{"int64mod", funcTag, 100},
{"uint64mod", funcTag, 101},
{"float64toint64", funcTag, 102},
{"float64touint64", funcTag, 103},
{"float64touint32", funcTag, 104},
{"int64tofloat64", funcTag, 105},
{"uint64tofloat64", funcTag, 106},
{"uint32tofloat64", funcTag, 107},
{"complex128div", funcTag, 108},
{"racefuncenter", funcTag, 109},
{"makeslice", funcTag, 94},
{"makeslice64", funcTag, 95},
{"growslice", funcTag, 96},
{"memmove", funcTag, 97},
{"memclrNoHeapPointers", funcTag, 98},
{"memclrHasPointers", funcTag, 98},
{"memequal", funcTag, 99},
{"memequal8", funcTag, 100},
{"memequal16", funcTag, 100},
{"memequal32", funcTag, 100},
{"memequal64", funcTag, 100},
{"memequal128", funcTag, 100},
{"int64div", funcTag, 101},
{"uint64div", funcTag, 102},
{"int64mod", funcTag, 101},
{"uint64mod", funcTag, 102},
{"float64toint64", funcTag, 103},
{"float64touint64", funcTag, 104},
{"float64touint32", funcTag, 105},
{"int64tofloat64", funcTag, 106},
{"uint64tofloat64", funcTag, 107},
{"uint32tofloat64", funcTag, 108},
{"complex128div", funcTag, 109},
{"racefuncenter", funcTag, 110},
{"racefuncexit", funcTag, 5},
{"raceread", funcTag, 109},
{"racewrite", funcTag, 109},
{"racereadrange", funcTag, 110},
{"racewriterange", funcTag, 110},
{"msanread", funcTag, 110},
{"msanwrite", funcTag, 110},
{"raceread", funcTag, 110},
{"racewrite", funcTag, 110},
{"racereadrange", funcTag, 111},
{"racewriterange", funcTag, 111},
{"msanread", funcTag, 111},
{"msanwrite", funcTag, 111},
{"support_popcnt", varTag, 11},
{"support_sse41", varTag, 11},
}
func runtimeTypes() []*types.Type {
var typs [111]*types.Type
var typs [112]*types.Type
typs[0] = types.Bytetype
typs[1] = types.NewPtr(typs[0])
typs[2] = types.Types[TANY]
@ -199,69 +200,70 @@ func runtimeTypes() []*types.Type {
typs[44] = types.NewArray(typs[40], 32)
typs[45] = types.NewPtr(typs[44])
typs[46] = functype(nil, []*Node{anonfield(typs[45]), anonfield(typs[21])}, []*Node{anonfield(typs[41])})
typs[47] = functype(nil, []*Node{anonfield(typs[21]), anonfield(typs[32])}, []*Node{anonfield(typs[40]), anonfield(typs[32])})
typs[48] = types.Types[TUINTPTR]
typs[49] = functype(nil, []*Node{anonfield(typs[2]), anonfield(typs[2]), anonfield(typs[48])}, []*Node{anonfield(typs[32])})
typs[50] = functype(nil, []*Node{anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[32])})
typs[51] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])})
typs[52] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])})
typs[53] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[11])})
typs[54] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil)
typs[55] = functype(nil, []*Node{anonfield(typs[1])}, nil)
typs[56] = types.NewPtr(typs[48])
typs[57] = types.Types[TUNSAFEPTR]
typs[58] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[57]), anonfield(typs[57])}, []*Node{anonfield(typs[11])})
typs[59] = types.Types[TUINT32]
typs[60] = functype(nil, nil, []*Node{anonfield(typs[59])})
typs[61] = types.NewMap(typs[2], typs[2])
typs[62] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[61])})
typs[63] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[3])}, []*Node{anonfield(typs[61])})
typs[64] = functype(nil, nil, []*Node{anonfield(typs[61])})
typs[65] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3])}, []*Node{anonfield(typs[3])})
typs[66] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[2])}, []*Node{anonfield(typs[3])})
typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])})
typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3])}, nil)
typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[2])}, nil)
typs[73] = functype(nil, []*Node{anonfield(typs[3])}, nil)
typs[74] = types.NewChan(typs[2], types.Cboth)
typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[74])})
typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[74])})
typs[77] = types.NewChan(typs[2], types.Crecv)
typs[78] = functype(nil, []*Node{anonfield(typs[77]), anonfield(typs[3])}, nil)
typs[79] = functype(nil, []*Node{anonfield(typs[77]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
typs[80] = types.NewChan(typs[2], types.Csend)
typs[81] = functype(nil, []*Node{anonfield(typs[80]), anonfield(typs[3])}, nil)
typs[82] = types.NewArray(typs[0], 3)
typs[83] = tostruct([]*Node{namedfield("enabled", typs[11]), namedfield("pad", typs[82]), namedfield("needed", typs[11]), namedfield("cgo", typs[11]), namedfield("alignme", typs[17])})
typs[84] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil)
typs[85] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil)
typs[86] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[32])})
typs[87] = functype(nil, []*Node{anonfield(typs[80]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
typs[88] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[77])}, []*Node{anonfield(typs[11])})
typs[89] = types.NewPtr(typs[11])
typs[90] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[89]), anonfield(typs[77])}, []*Node{anonfield(typs[11])})
typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[32]), anonfield(typs[11])})
typs[92] = types.NewSlice(typs[2])
typs[93] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[32])}, []*Node{anonfield(typs[92])})
typs[94] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[92])})
typs[95] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[92]), anonfield(typs[32])}, []*Node{anonfield(typs[92])})
typs[96] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[48])}, nil)
typs[97] = functype(nil, []*Node{anonfield(typs[57]), anonfield(typs[48])}, nil)
typs[98] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[48])}, []*Node{anonfield(typs[11])})
typs[99] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
typs[100] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[15])})
typs[101] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[17])}, []*Node{anonfield(typs[17])})
typs[102] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[15])})
typs[103] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[17])})
typs[104] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[59])})
typs[105] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[13])})
typs[106] = functype(nil, []*Node{anonfield(typs[17])}, []*Node{anonfield(typs[13])})
typs[107] = functype(nil, []*Node{anonfield(typs[59])}, []*Node{anonfield(typs[13])})
typs[108] = functype(nil, []*Node{anonfield(typs[19]), anonfield(typs[19])}, []*Node{anonfield(typs[19])})
typs[109] = functype(nil, []*Node{anonfield(typs[48])}, nil)
typs[110] = functype(nil, []*Node{anonfield(typs[48]), anonfield(typs[48])}, nil)
typs[47] = types.Types[TUINTPTR]
typs[48] = functype(nil, []*Node{anonfield(typs[2]), anonfield(typs[2]), anonfield(typs[47])}, []*Node{anonfield(typs[32])})
typs[49] = functype(nil, []*Node{anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[32])})
typs[50] = functype(nil, []*Node{anonfield(typs[21]), anonfield(typs[32])}, []*Node{anonfield(typs[40]), anonfield(typs[32])})
typs[51] = functype(nil, []*Node{anonfield(typs[21])}, []*Node{anonfield(typs[32])})
typs[52] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2])})
typs[53] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, []*Node{anonfield(typs[2])})
typs[54] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2])}, []*Node{anonfield(typs[2]), anonfield(typs[11])})
typs[55] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[1])}, nil)
typs[56] = functype(nil, []*Node{anonfield(typs[1])}, nil)
typs[57] = types.NewPtr(typs[47])
typs[58] = types.Types[TUNSAFEPTR]
typs[59] = functype(nil, []*Node{anonfield(typs[57]), anonfield(typs[58]), anonfield(typs[58])}, []*Node{anonfield(typs[11])})
typs[60] = types.Types[TUINT32]
typs[61] = functype(nil, nil, []*Node{anonfield(typs[60])})
typs[62] = types.NewMap(typs[2], typs[2])
typs[63] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[62])})
typs[64] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[3])}, []*Node{anonfield(typs[62])})
typs[65] = functype(nil, nil, []*Node{anonfield(typs[62])})
typs[66] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3])}, []*Node{anonfield(typs[3])})
typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[2])}, []*Node{anonfield(typs[3])})
typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])})
typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[3])}, nil)
typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[62]), anonfield(typs[2])}, nil)
typs[74] = functype(nil, []*Node{anonfield(typs[3])}, nil)
typs[75] = types.NewChan(typs[2], types.Cboth)
typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[75])})
typs[77] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[75])})
typs[78] = types.NewChan(typs[2], types.Crecv)
typs[79] = functype(nil, []*Node{anonfield(typs[78]), anonfield(typs[3])}, nil)
typs[80] = functype(nil, []*Node{anonfield(typs[78]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
typs[81] = types.NewChan(typs[2], types.Csend)
typs[82] = functype(nil, []*Node{anonfield(typs[81]), anonfield(typs[3])}, nil)
typs[83] = types.NewArray(typs[0], 3)
typs[84] = tostruct([]*Node{namedfield("enabled", typs[11]), namedfield("pad", typs[83]), namedfield("needed", typs[11]), namedfield("cgo", typs[11]), namedfield("alignme", typs[17])})
typs[85] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil)
typs[86] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil)
typs[87] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[32])})
typs[88] = functype(nil, []*Node{anonfield(typs[81]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
typs[89] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[78])}, []*Node{anonfield(typs[11])})
typs[90] = types.NewPtr(typs[11])
typs[91] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[90]), anonfield(typs[78])}, []*Node{anonfield(typs[11])})
typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[32]), anonfield(typs[11])})
typs[93] = types.NewSlice(typs[2])
typs[94] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[32])}, []*Node{anonfield(typs[93])})
typs[95] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[93])})
typs[96] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[93]), anonfield(typs[32])}, []*Node{anonfield(typs[93])})
typs[97] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[47])}, nil)
typs[98] = functype(nil, []*Node{anonfield(typs[58]), anonfield(typs[47])}, nil)
typs[99] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[47])}, []*Node{anonfield(typs[11])})
typs[100] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
typs[101] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[15])})
typs[102] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[17])}, []*Node{anonfield(typs[17])})
typs[103] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[15])})
typs[104] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[17])})
typs[105] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[60])})
typs[106] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[13])})
typs[107] = functype(nil, []*Node{anonfield(typs[17])}, []*Node{anonfield(typs[13])})
typs[108] = functype(nil, []*Node{anonfield(typs[60])}, []*Node{anonfield(typs[13])})
typs[109] = functype(nil, []*Node{anonfield(typs[19]), anonfield(typs[19])}, []*Node{anonfield(typs[19])})
typs[110] = functype(nil, []*Node{anonfield(typs[47])}, nil)
typs[111] = functype(nil, []*Node{anonfield(typs[47]), anonfield(typs[47])}, nil)
return typs[:]
}

View file

@ -55,10 +55,12 @@ func slicebytetostringtmp([]byte) string
func slicerunetostring(*[32]byte, []rune) string
func stringtoslicebyte(*[32]byte, string) []byte
func stringtoslicerune(*[32]rune, string) []rune
func decoderune(string, int) (retv rune, retk int)
func slicecopy(to any, fr any, wid uintptr) int
func slicestringcopy(to any, fr any) int
func decoderune(string, int) (retv rune, retk int)
func countrunes(string) int
// interface conversions
func convI2I(typ *byte, elem any) (ret any)

View file

@ -1098,7 +1098,14 @@ func (o *Order) expr(n, lhs *Node) *Node {
OSTRARRAYBYTE,
OSTRARRAYBYTETMP,
OSTRARRAYRUNE:
o.call(n)
if isRuneCount(n) {
// len([]rune(s)) is rewritten to runtime.countrunes(s) later.
n.Left.Left = o.expr(n.Left.Left, nil)
} else {
o.call(n)
}
if lhs == nil || lhs.Op != ONAME || instrumenting {
n = o.copyExpr(n, n.Type, false)
}

View file

@ -538,6 +538,12 @@ opswitch:
n.Left = walkexpr(n.Left, init)
case OLEN, OCAP:
if isRuneCount(n) {
// Replace len([]rune(string)) with runtime.countrunes(string).
n = mkcall("countrunes", n.Type, init, conv(n.Left.Left, types.Types[TSTRING]))
break
}
n.Left = walkexpr(n.Left, init)
// replace len(*[10]int) with 10.
@ -4085,3 +4091,9 @@ func canMergeLoads() bool {
}
return false
}
// isRuneCount reports whether n is of the form len([]rune(string)).
// These are optimized into a call to runtime.runecount.
func isRuneCount(n *Node) bool {
return Debug['N'] == 0 && !instrumenting && n.Op == OLEN && n.Left.Op == OSTRARRAYRUNE
}

View file

@ -9,6 +9,7 @@ import (
"strconv"
"strings"
"testing"
"unicode/utf8"
)
// Strings and slices that don't escape and fit into tmpBuf are stack allocated,
@ -110,6 +111,43 @@ var stringdata = []struct{ name, data string }{
{"MixedLength", "$Ѐࠀက퀀𐀀\U00040000\U0010FFFF"},
}
var sinkInt int
func BenchmarkRuneCount(b *testing.B) {
// Each sub-benchmark counts the runes in a string in a different way.
b.Run("lenruneslice", func(b *testing.B) {
for _, sd := range stringdata {
b.Run(sd.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkInt += len([]rune(sd.data))
}
})
}
})
b.Run("rangeloop", func(b *testing.B) {
for _, sd := range stringdata {
b.Run(sd.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
n := 0
for range sd.data {
n++
}
sinkInt += n
}
})
}
})
b.Run("utf8.RuneCountInString", func(b *testing.B) {
for _, sd := range stringdata {
b.Run(sd.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
sinkInt += utf8.RuneCountInString(sd.data)
}
})
}
})
}
func BenchmarkRuneIterate(b *testing.B) {
b.Run("range", func(b *testing.B) {
for _, sd := range stringdata {

View file

@ -39,6 +39,15 @@ const (
hicb = 0xBF // 1011 1111
)
// countrunes returns the number of runes in s.
func countrunes(s string) int {
n := 0
for range s {
n++
}
return n
}
// decoderune returns the non-ASCII rune at the start of
// s[k:] and the index after the rune in s.
//

View file

@ -212,14 +212,25 @@ func TestSequencing(t *testing.T) {
}
}
// Check that a range loop and a []int conversion visit the same runes.
func runtimeRuneCount(s string) int {
return len([]rune(s)) // Replaced by gc with call to runtime.countrunes(s).
}
// Check that a range loop, len([]rune(string)) optimization and
// []rune conversions visit the same runes.
// Not really a test of this package, but the assumption is used here and
// it's good to verify
func TestIntConversion(t *testing.T) {
// it's good to verify.
func TestRuntimeConversion(t *testing.T) {
for _, ts := range testStrings {
count := RuneCountInString(ts)
if n := runtimeRuneCount(ts); n != count {
t.Errorf("%q: len([]rune()) counted %d runes; got %d from RuneCountInString", ts, n, count)
break
}
runes := []rune(ts)
if RuneCountInString(ts) != len(runes) {
t.Errorf("%q: expected %d runes; got %d", ts, len(runes), RuneCountInString(ts))
if n := len(runes); n != count {
t.Errorf("%q: []rune() has length %d; got %d from RuneCountInString", ts, n, count)
break
}
i := 0

15
test/codegen/strings.go Normal file
View file

@ -0,0 +1,15 @@
// asmcheck
// Copyright 2018 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 codegen
// This file contains code generation tests related to the handling of
// string types.
func CountRunes(s string) int { // Issue #24923
// amd64:`.*countrunes`
return len([]rune(s))
}