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 {
......
...@@ -212,7 +212,7 @@ func foo21() func() int { ...@@ -212,7 +212,7 @@ func foo21() func() int {
func foo21a() func() int { func foo21a() func() int {
x := 42 // ERROR "moved to heap: x" x := 42 // ERROR "moved to heap: x"
return func() int { // ERROR "func literal escapes to heap" return func() int { // ERROR "func literal escapes to heap"
x++ // ERROR "&x escapes to heap" x++ // ERROR "&x escapes to heap"
return x return x
} }
} }
...@@ -1560,14 +1560,14 @@ func ptrlitNoescape() { ...@@ -1560,14 +1560,14 @@ func ptrlitNoescape() {
func ptrlitNoEscape2() { func ptrlitNoEscape2() {
// Literal does not escape, but element does. // Literal does not escape, but element does.
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
x := &Lit{&i} // ERROR "&Lit literal does not escape" "&i escapes to heap" x := &Lit{&i} // ERROR "&Lit literal does not escape" "&i escapes to heap"
sink = *x sink = *x
} }
func ptrlitEscape() { func ptrlitEscape() {
// Both literal and element escape. // Both literal and element escape.
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
x := &Lit{&i} // ERROR "&Lit literal escapes to heap" "&i escapes to heap" x := &Lit{&i} // ERROR "&Lit literal escapes to heap" "&i escapes to heap"
sink = x sink = x
} }
...@@ -1619,7 +1619,7 @@ type StructWithString struct { ...@@ -1619,7 +1619,7 @@ type StructWithString struct {
// to just x, and thus &i looks escaping. // to just x, and thus &i looks escaping.
func fieldFlowTracking() { func fieldFlowTracking() {
var x StructWithString var x StructWithString
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
x.p = &i // ERROR "&i escapes to heap" x.p = &i // ERROR "&i escapes to heap"
sink = x.s sink = x.s
} }
...@@ -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"
}
...@@ -212,7 +212,7 @@ func foo21() func() int { ...@@ -212,7 +212,7 @@ func foo21() func() int {
func foo21a() func() int { func foo21a() func() int {
x := 42 // ERROR "moved to heap: x" x := 42 // ERROR "moved to heap: x"
return func() int { // ERROR "func literal escapes to heap" return func() int { // ERROR "func literal escapes to heap"
x++ // ERROR "&x escapes to heap" x++ // ERROR "&x escapes to heap"
return x return x
} }
} }
...@@ -1560,14 +1560,14 @@ func ptrlitNoescape() { ...@@ -1560,14 +1560,14 @@ func ptrlitNoescape() {
func ptrlitNoEscape2() { func ptrlitNoEscape2() {
// Literal does not escape, but element does. // Literal does not escape, but element does.
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
x := &Lit{&i} // ERROR "&Lit literal does not escape" "&i escapes to heap" x := &Lit{&i} // ERROR "&Lit literal does not escape" "&i escapes to heap"
sink = *x sink = *x
} }
func ptrlitEscape() { func ptrlitEscape() {
// Both literal and element escape. // Both literal and element escape.
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
x := &Lit{&i} // ERROR "&Lit literal escapes to heap" "&i escapes to heap" x := &Lit{&i} // ERROR "&Lit literal escapes to heap" "&i escapes to heap"
sink = x sink = x
} }
...@@ -1619,7 +1619,7 @@ type StructWithString struct { ...@@ -1619,7 +1619,7 @@ type StructWithString struct {
// to just x, and thus &i looks escaping. // to just x, and thus &i looks escaping.
func fieldFlowTracking() { func fieldFlowTracking() {
var x StructWithString var x StructWithString
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
x.p = &i // ERROR "&i escapes to heap" x.p = &i // ERROR "&i escapes to heap"
sink = x.s sink = x.s
} }
...@@ -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