cmd/gc: allocate buffers for non-escaping string conversions on stack

Support the following conversions in escape analysis:
[]rune("foo")
[]byte("foo")
string([]rune{})

If the result does not escape, allocate temp buffer on stack
and pass it to runtime functions.

Change-Id: I1d075907eab8b0109ad7ad1878104b02b3d5c690
Reviewed-on: https://go-review.googlesource.com/3590
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Dmitry Vyukov 2015-01-30 09:14:13 +03:00
parent cdc2b0568f
commit 9568126f35
7 changed files with 159 additions and 36 deletions

View File

@ -36,10 +36,10 @@ char *runtimeimport =
"func @\"\".intstring (? *[4]byte, ? int64) (? string)\n" "func @\"\".intstring (? *[4]byte, ? int64) (? string)\n"
"func @\"\".slicebytetostring (? *[32]byte, ? []byte) (? string)\n" "func @\"\".slicebytetostring (? *[32]byte, ? []byte) (? string)\n"
"func @\"\".slicebytetostringtmp (? []byte) (? string)\n" "func @\"\".slicebytetostringtmp (? []byte) (? string)\n"
"func @\"\".slicerunetostring (? []rune) (? string)\n" "func @\"\".slicerunetostring (? *[32]byte, ? []rune) (? string)\n"
"func @\"\".stringtoslicebyte (? string) (? []byte)\n" "func @\"\".stringtoslicebyte (? *[32]byte, ? string) (? []byte)\n"
"func @\"\".stringtoslicebytetmp (? string) (? []byte)\n" "func @\"\".stringtoslicebytetmp (? string) (? []byte)\n"
"func @\"\".stringtoslicerune (? string) (? []rune)\n" "func @\"\".stringtoslicerune (? *[32]rune, ? string) (? []rune)\n"
"func @\"\".stringiter (? string, ? int) (? int)\n" "func @\"\".stringiter (? string, ? int) (? int)\n"
"func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n" "func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n"
"func @\"\".slicecopy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n" "func @\"\".slicecopy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n"

View File

@ -693,12 +693,10 @@ esc(EscState *e, Node *n, Node *up)
case OMAKEMAP: case OMAKEMAP:
case OMAKESLICE: case OMAKESLICE:
case ONEW: case ONEW:
n->escloopdepth = e->loopdepth; case OARRAYRUNESTR:
n->esc = EscNone; // until proven otherwise
e->noesc = list(e->noesc, n);
break;
case OARRAYBYTESTR: case OARRAYBYTESTR:
case OSTRARRAYRUNE:
case OSTRARRAYBYTE:
case ORUNESTR: case ORUNESTR:
n->escloopdepth = e->loopdepth; n->escloopdepth = e->loopdepth;
n->esc = EscNone; // until proven otherwise n->esc = EscNone; // until proven otherwise
@ -824,7 +822,10 @@ escassign(EscState *e, Node *dst, Node *src)
case OMAKECHAN: case OMAKECHAN:
case OMAKEMAP: case OMAKEMAP:
case OMAKESLICE: case OMAKESLICE:
case OARRAYRUNESTR:
case OARRAYBYTESTR: case OARRAYBYTESTR:
case OSTRARRAYRUNE:
case OSTRARRAYBYTE:
case OADDSTR: case OADDSTR:
case ONEW: case ONEW:
case OCLOSURE: case OCLOSURE:
@ -1249,7 +1250,10 @@ escwalk(EscState *e, int level, Node *dst, Node *src)
case OMAKECHAN: case OMAKECHAN:
case OMAKEMAP: case OMAKEMAP:
case OMAKESLICE: case OMAKESLICE:
case OARRAYRUNESTR:
case OARRAYBYTESTR: case OARRAYBYTESTR:
case OSTRARRAYRUNE:
case OSTRARRAYBYTE:
case OADDSTR: case OADDSTR:
case OMAPLIT: case OMAPLIT:
case ONEW: case ONEW:

View File

@ -50,10 +50,10 @@ func eqstring(string, string) bool
func intstring(*[4]byte, int64) string func intstring(*[4]byte, int64) string
func slicebytetostring(*[32]byte, []byte) string func slicebytetostring(*[32]byte, []byte) string
func slicebytetostringtmp([]byte) string func slicebytetostringtmp([]byte) string
func slicerunetostring([]rune) string func slicerunetostring(*[32]byte, []rune) string
func stringtoslicebyte(string) []byte func stringtoslicebyte(*[32]byte, string) []byte
func stringtoslicebytetmp(string) []byte func stringtoslicebytetmp(string) []byte
func stringtoslicerune(string) []rune func stringtoslicerune(*[32]rune, string) []rune
func stringiter(string, int) int func stringiter(string, int) int
func stringiter2(string, int) (retk int, retv rune) func stringiter2(string, int) (retk int, retv rune)
func slicecopy(to any, fr any, wid uintptr) int func slicecopy(to any, fr any, wid uintptr) int

View File

@ -1398,13 +1398,25 @@ walkexpr(Node **np, NodeList **init)
goto ret; goto ret;
case OARRAYRUNESTR: case OARRAYRUNESTR:
// slicerunetostring([]rune) string; // slicerunetostring(*[32]byte, []rune) string;
n = mkcall("slicerunetostring", n->type, init, n->left); a = nodnil();
if(n->esc == EscNone) {
// Create temporary buffer for string on stack.
t = aindex(nodintconst(tmpstringbufsize), types[TUINT8]);
a = nod(OADDR, temp(t), N);
}
n = mkcall("slicerunetostring", n->type, init, a, n->left);
goto ret; goto ret;
case OSTRARRAYBYTE: case OSTRARRAYBYTE:
// stringtoslicebyte(string) []byte; // stringtoslicebyte(*32[byte], string) []byte;
n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING])); a = nodnil();
if(n->esc == EscNone) {
// Create temporary buffer for slice on stack.
t = aindex(nodintconst(tmpstringbufsize), types[TUINT8]);
a = nod(OADDR, temp(t), N);
}
n = mkcall("stringtoslicebyte", n->type, init, a, conv(n->left, types[TSTRING]));
goto ret; goto ret;
case OSTRARRAYBYTETMP: case OSTRARRAYBYTETMP:
@ -1413,8 +1425,14 @@ walkexpr(Node **np, NodeList **init)
goto ret; goto ret;
case OSTRARRAYRUNE: case OSTRARRAYRUNE:
// stringtoslicerune(string) []rune // stringtoslicerune(*[32]rune, string) []rune
n = mkcall("stringtoslicerune", n->type, init, n->left); a = nodnil();
if(n->esc == EscNone) {
// Create temporary buffer for slice on stack.
t = aindex(nodintconst(tmpstringbufsize), types[TINT32]);
a = nod(OADDR, temp(t), N);
}
n = mkcall("stringtoslicerune", n->type, init, a, n->left);
goto ret; goto ret;
case OCMPIFACE: case OCMPIFACE:

View File

@ -129,8 +129,13 @@ func slicebytetostringtmp(b []byte) string {
return *(*string)(unsafe.Pointer(&b)) return *(*string)(unsafe.Pointer(&b))
} }
func stringtoslicebyte(s string) []byte { func stringtoslicebyte(buf *tmpBuf, s string) []byte {
b := rawbyteslice(len(s)) var b []byte
if buf != nil && len(s) <= len(buf) {
b = buf[:len(s)]
} else {
b = rawbyteslice(len(s))
}
copy(b, s) copy(b, s)
return b return b
} }
@ -147,7 +152,7 @@ func stringtoslicebytetmp(s string) []byte {
return *(*[]byte)(unsafe.Pointer(&ret)) return *(*[]byte)(unsafe.Pointer(&ret))
} }
func stringtoslicerune(s string) []rune { func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune {
// two passes. // two passes.
// unlike slicerunetostring, no race because strings are immutable. // unlike slicerunetostring, no race because strings are immutable.
n := 0 n := 0
@ -157,7 +162,12 @@ func stringtoslicerune(s string) []rune {
s = s[k:] s = s[k:]
n++ n++
} }
a := rawruneslice(n) var a []rune
if buf != nil && n <= len(buf) {
a = buf[:n]
} else {
a = rawruneslice(n)
}
n = 0 n = 0
for len(t) > 0 { for len(t) > 0 {
r, k := charntorune(t) r, k := charntorune(t)
@ -168,7 +178,7 @@ func stringtoslicerune(s string) []rune {
return a return a
} }
func slicerunetostring(a []rune) string { func slicerunetostring(buf *tmpBuf, a []rune) string {
if raceenabled && len(a) > 0 { if raceenabled && len(a) > 0 {
racereadrangepc(unsafe.Pointer(&a[0]), racereadrangepc(unsafe.Pointer(&a[0]),
uintptr(len(a))*unsafe.Sizeof(a[0]), uintptr(len(a))*unsafe.Sizeof(a[0]),
@ -180,7 +190,7 @@ func slicerunetostring(a []rune) string {
for _, r := range a { for _, r := range a {
size1 += runetochar(dum[:], r) size1 += runetochar(dum[:], r)
} }
s, b := rawstring(size1 + 3) s, b := rawstringtmp(buf, size1+3)
size2 := 0 size2 := 0
for _, r := range a { for _, r := range a {
// check for race // check for race
@ -309,11 +319,6 @@ func gobytes(p *byte, n int) []byte {
return x return x
} }
func gostringsize(n int) string {
s, _ := rawstring(n)
return s
}
func gostring(p *byte) string { func gostring(p *byte) string {
l := findnull(p) l := findnull(p)
if l == 0 { if l == 0 {

View File

@ -1703,3 +1703,51 @@ func intstring2() {
s := string(x) // ERROR "string\(x\) escapes to heap" "moved to heap: s" s := string(x) // ERROR "string\(x\) escapes to heap" "moved to heap: s"
sink = &s // ERROR "&s escapes to heap" sink = &s // ERROR "&s escapes to heap"
} }
func stringtoslicebyte0() {
s := "foo"
x := []byte(s) // ERROR "\(\[\]byte\)\(s\) does not escape"
_ = x
}
func stringtoslicebyte1() []byte {
s := "foo"
return []byte(s) // ERROR "\(\[\]byte\)\(s\) escapes to heap"
}
func stringtoslicebyte2() {
s := "foo"
sink = []byte(s) // ERROR "\(\[\]byte\)\(s\) escapes to heap"
}
func stringtoslicerune0() {
s := "foo"
x := []rune(s) // ERROR "\(\[\]rune\)\(s\) does not escape"
_ = x
}
func stringtoslicerune1() []rune {
s := "foo"
return []rune(s) // ERROR "\(\[\]rune\)\(s\) escapes to heap"
}
func stringtoslicerune2() {
s := "foo"
sink = []rune(s) // ERROR "\(\[\]rune\)\(s\) escapes to heap"
}
func slicerunetostring0() {
r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape"
s := string(r) // ERROR "string\(r\) does not escape"
_ = s
}
func slicerunetostring1() string {
r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape"
return string(r) // ERROR "string\(r\) escapes to heap"
}
func slicerunetostring2() {
r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape"
sink = string(r) // ERROR "string\(r\) escapes to heap"
}

View File

@ -1703,3 +1703,51 @@ func intstring2() {
s := string(x) // ERROR "string\(x\) escapes to heap" "moved to heap: s" s := string(x) // ERROR "string\(x\) escapes to heap" "moved to heap: s"
sink = &s // ERROR "&s escapes to heap" sink = &s // ERROR "&s escapes to heap"
} }
func stringtoslicebyte0() {
s := "foo"
x := []byte(s) // ERROR "\(\[\]byte\)\(s\) does not escape"
_ = x
}
func stringtoslicebyte1() []byte {
s := "foo"
return []byte(s) // ERROR "\(\[\]byte\)\(s\) escapes to heap"
}
func stringtoslicebyte2() {
s := "foo"
sink = []byte(s) // ERROR "\(\[\]byte\)\(s\) escapes to heap"
}
func stringtoslicerune0() {
s := "foo"
x := []rune(s) // ERROR "\(\[\]rune\)\(s\) does not escape"
_ = x
}
func stringtoslicerune1() []rune {
s := "foo"
return []rune(s) // ERROR "\(\[\]rune\)\(s\) escapes to heap"
}
func stringtoslicerune2() {
s := "foo"
sink = []rune(s) // ERROR "\(\[\]rune\)\(s\) escapes to heap"
}
func slicerunetostring0() {
r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape"
s := string(r) // ERROR "string\(r\) does not escape"
_ = s
}
func slicerunetostring1() string {
r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape"
return string(r) // ERROR "string\(r\) escapes to heap"
}
func slicerunetostring2() {
r := []rune{1, 2, 3} // ERROR "\[\]rune literal does not escape"
sink = string(r) // ERROR "string\(r\) escapes to heap"
}