Commit 9568126f authored by Dmitry Vyukov's avatar Dmitry Vyukov

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/3590Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent cdc2b056
...@@ -36,10 +36,10 @@ char *runtimeimport = ...@@ -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"
......
...@@ -693,12 +693,10 @@ esc(EscState *e, Node *n, Node *up) ...@@ -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) ...@@ -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) ...@@ -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:
......
...@@ -50,10 +50,10 @@ func eqstring(string, string) bool ...@@ -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
......
...@@ -1398,13 +1398,25 @@ walkexpr(Node **np, NodeList **init) ...@@ -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) ...@@ -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:
......
...@@ -129,8 +129,13 @@ func slicebytetostringtmp(b []byte) string { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 {
......
...@@ -1703,3 +1703,51 @@ func intstring2() { ...@@ -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"
}
...@@ -1703,3 +1703,51 @@ func intstring2() { ...@@ -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"
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment