mirror of https://github.com/golang/go.git
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:
parent
cdc2b0568f
commit
9568126f35
|
|
@ -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)
|
||||||
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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
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:
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
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"
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue