]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: optimize len([]rune(string))
authorMartin Möhrmann <moehrmann@google.com>
Tue, 24 Apr 2018 13:13:08 +0000 (15:13 +0200)
committerMartin Möhrmann <moehrmann@google.com>
Sun, 6 May 2018 05:31:01 +0000 (05:31 +0000)
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>
src/cmd/compile/internal/gc/builtin.go
src/cmd/compile/internal/gc/builtin/runtime.go
src/cmd/compile/internal/gc/order.go
src/cmd/compile/internal/gc/walk.go
src/runtime/string_test.go
src/runtime/utf8.go
src/unicode/utf8/utf8_test.go
test/codegen/strings.go [new file with mode: 0644]

index 4259fb415305bacbf9e10351b161cc679e3a29c5..3ca1adc1f7669bd7b8630d04a7f8e742431e90bb 100644 (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[:]
 }
index ae1850c72f46d1dcf32039410c7b165ef10c373b..1d3f17c0d1a2d91ddc33fac8ebb77de786dcd441 100644 (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)
 
index 1a10587797c2d566d84570e3170e809c66f62413..45a3b5cc42632ef3c29c984ac488672466fd4e40 100644 (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)
                }
index 3046b9dda872b6261097536866f5df58b024cd83..edf06579234dc3515e5cf3ab908d0da7274eee06 100644 (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
+}
index c6e925d2e0726cb4f0b30e942d021a7664786746..678ff003636f987c01001d5e806635c0394ac05b 100644 (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 {
index 24ef179214037e381239f50522600d2f23ec448c..6bf596581ddb9bf26a90dbfa76f89099df698661 100644 (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.
 //
index dc9c4251bd92a8077a5d8db79a8b361abd4fb4e5..359461bd0579102f3a0902aecad2799ae772d9e5 100644 (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
diff --git a/test/codegen/strings.go b/test/codegen/strings.go
new file mode 100644 (file)
index 0000000..ccb6bd4
--- /dev/null
@@ -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))
+}