mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: fix stringtoslicebytetmp optimization
Fixes #14973. Change-Id: Iea68c9deca9429bde465c9ae05639209fe0ccf72 Reviewed-on: https://go-review.googlesource.com/21175 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
4ffa5eb876
commit
995fb0319e
2 changed files with 28 additions and 23 deletions
|
|
@ -705,28 +705,31 @@ func orderstmt(n *Node, order *Order) {
|
||||||
order.out = append(order.out, n)
|
order.out = append(order.out, n)
|
||||||
cleantemp(t, order)
|
cleantemp(t, order)
|
||||||
|
|
||||||
// n->right is the expression being ranged over.
|
case ORANGE:
|
||||||
|
// n.Right is the expression being ranged over.
|
||||||
// order it, and then make a copy if we need one.
|
// order it, and then make a copy if we need one.
|
||||||
// We almost always do, to ensure that we don't
|
// We almost always do, to ensure that we don't
|
||||||
// see any value changes made during the loop.
|
// see any value changes made during the loop.
|
||||||
// Usually the copy is cheap (e.g., array pointer, chan, slice, string are all tiny).
|
// Usually the copy is cheap (e.g., array pointer,
|
||||||
// The exception is ranging over an array value (not a slice, not a pointer to array),
|
// chan, slice, string are all tiny).
|
||||||
|
// The exception is ranging over an array value
|
||||||
|
// (not a slice, not a pointer to array),
|
||||||
// which must make a copy to avoid seeing updates made during
|
// which must make a copy to avoid seeing updates made during
|
||||||
// the range body. Ranging over an array value is uncommon though.
|
// the range body. Ranging over an array value is uncommon though.
|
||||||
case ORANGE:
|
|
||||||
t := marktemp(order)
|
|
||||||
|
|
||||||
|
// Mark []byte(str) range expression to reuse string backing storage.
|
||||||
|
// It is safe because the storage cannot be mutated.
|
||||||
|
if n.Right.Op == OSTRARRAYBYTE {
|
||||||
|
n.Right.Op = OSTRARRAYBYTETMP
|
||||||
|
}
|
||||||
|
|
||||||
|
t := marktemp(order)
|
||||||
n.Right = orderexpr(n.Right, order, nil)
|
n.Right = orderexpr(n.Right, order, nil)
|
||||||
switch n.Type.Etype {
|
switch n.Type.Etype {
|
||||||
default:
|
default:
|
||||||
Fatalf("orderstmt range %v", n.Type)
|
Fatalf("orderstmt range %v", n.Type)
|
||||||
|
|
||||||
// Mark []byte(str) range expression to reuse string backing storage.
|
|
||||||
// It is safe because the storage cannot be mutated.
|
|
||||||
case TARRAY:
|
case TARRAY:
|
||||||
if n.Right.Op == OSTRARRAYBYTE {
|
|
||||||
n.Right.Op = OSTRARRAYBYTETMP
|
|
||||||
}
|
|
||||||
if n.List.Len() < 2 || isblank(n.List.Second()) {
|
if n.List.Len() < 2 || isblank(n.List.Second()) {
|
||||||
// for i := range x will only use x once, to compute len(x).
|
// for i := range x will only use x once, to compute len(x).
|
||||||
// No need to copy it.
|
// No need to copy it.
|
||||||
|
|
@ -734,10 +737,9 @@ func orderstmt(n *Node, order *Order) {
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
|
|
||||||
|
case TCHAN, TSTRING:
|
||||||
// chan, string, slice, array ranges use value multiple times.
|
// chan, string, slice, array ranges use value multiple times.
|
||||||
// make copy.
|
// make copy.
|
||||||
// fall through
|
|
||||||
case TCHAN, TSTRING:
|
|
||||||
r := n.Right
|
r := n.Right
|
||||||
|
|
||||||
if r.Type.Etype == TSTRING && r.Type != Types[TSTRING] {
|
if r.Type.Etype == TSTRING && r.Type != Types[TSTRING] {
|
||||||
|
|
@ -748,12 +750,11 @@ func orderstmt(n *Node, order *Order) {
|
||||||
|
|
||||||
n.Right = ordercopyexpr(r, r.Type, order, 0)
|
n.Right = ordercopyexpr(r, r.Type, order, 0)
|
||||||
|
|
||||||
|
case TMAP:
|
||||||
// copy the map value in case it is a map literal.
|
// copy the map value in case it is a map literal.
|
||||||
// TODO(rsc): Make tmp = literal expressions reuse tmp.
|
// TODO(rsc): Make tmp = literal expressions reuse tmp.
|
||||||
// For maps tmp is just one word so it hardly matters.
|
// For maps tmp is just one word so it hardly matters.
|
||||||
case TMAP:
|
|
||||||
r := n.Right
|
r := n.Right
|
||||||
|
|
||||||
n.Right = ordercopyexpr(r, r.Type, order, 0)
|
n.Right = ordercopyexpr(r, r.Type, order, 0)
|
||||||
|
|
||||||
// n->alloc is the temp for the iterator.
|
// n->alloc is the temp for the iterator.
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Strings and slices that don't escape and fit into tmpBuf are stack allocated,
|
||||||
|
// which defeats using AllocsPerRun to test other optimizations.
|
||||||
|
const sizeNoStack = 100
|
||||||
|
|
||||||
func BenchmarkCompareStringEqual(b *testing.B) {
|
func BenchmarkCompareStringEqual(b *testing.B) {
|
||||||
bytes := []byte("Hello Gophers!")
|
bytes := []byte("Hello Gophers!")
|
||||||
s1, s2 := string(bytes), string(bytes)
|
s1, s2 := string(bytes), string(bytes)
|
||||||
|
|
@ -158,7 +162,7 @@ func TestGostringnocopy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareTempString(t *testing.T) {
|
func TestCompareTempString(t *testing.T) {
|
||||||
s := "foo"
|
s := strings.Repeat("x", sizeNoStack)
|
||||||
b := []byte(s)
|
b := []byte(s)
|
||||||
n := testing.AllocsPerRun(1000, func() {
|
n := testing.AllocsPerRun(1000, func() {
|
||||||
if string(b) != s {
|
if string(b) != s {
|
||||||
|
|
@ -221,7 +225,7 @@ func TestIntStringAllocs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRangeStringCast(t *testing.T) {
|
func TestRangeStringCast(t *testing.T) {
|
||||||
s := "abc"
|
s := strings.Repeat("x", sizeNoStack)
|
||||||
n := testing.AllocsPerRun(1000, func() {
|
n := testing.AllocsPerRun(1000, func() {
|
||||||
for i, c := range []byte(s) {
|
for i, c := range []byte(s) {
|
||||||
if c != s[i] {
|
if c != s[i] {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue