mirror of
https://github.com/golang/go
synced 2024-11-22 15:04:52 -07:00
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 @\"\".slicebytetostring (? *[32]byte, ? []byte) (? string)\n"
|
||||
"func @\"\".slicebytetostringtmp (? []byte) (? string)\n"
|
||||
"func @\"\".slicerunetostring (? []rune) (? string)\n"
|
||||
"func @\"\".stringtoslicebyte (? string) (? []byte)\n"
|
||||
"func @\"\".slicerunetostring (? *[32]byte, ? []rune) (? string)\n"
|
||||
"func @\"\".stringtoslicebyte (? *[32]byte, ? 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 @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\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 OMAKESLICE:
|
||||
case ONEW:
|
||||
n->escloopdepth = e->loopdepth;
|
||||
n->esc = EscNone; // until proven otherwise
|
||||
e->noesc = list(e->noesc, n);
|
||||
break;
|
||||
|
||||
case OARRAYRUNESTR:
|
||||
case OARRAYBYTESTR:
|
||||
case OSTRARRAYRUNE:
|
||||
case OSTRARRAYBYTE:
|
||||
case ORUNESTR:
|
||||
n->escloopdepth = e->loopdepth;
|
||||
n->esc = EscNone; // until proven otherwise
|
||||
@ -824,7 +822,10 @@ escassign(EscState *e, Node *dst, Node *src)
|
||||
case OMAKECHAN:
|
||||
case OMAKEMAP:
|
||||
case OMAKESLICE:
|
||||
case OARRAYRUNESTR:
|
||||
case OARRAYBYTESTR:
|
||||
case OSTRARRAYRUNE:
|
||||
case OSTRARRAYBYTE:
|
||||
case OADDSTR:
|
||||
case ONEW:
|
||||
case OCLOSURE:
|
||||
@ -1249,7 +1250,10 @@ escwalk(EscState *e, int level, Node *dst, Node *src)
|
||||
case OMAKECHAN:
|
||||
case OMAKEMAP:
|
||||
case OMAKESLICE:
|
||||
case OARRAYRUNESTR:
|
||||
case OARRAYBYTESTR:
|
||||
case OSTRARRAYRUNE:
|
||||
case OSTRARRAYBYTE:
|
||||
case OADDSTR:
|
||||
case OMAPLIT:
|
||||
case ONEW:
|
||||
|
@ -50,10 +50,10 @@ func eqstring(string, string) bool
|
||||
func intstring(*[4]byte, int64) string
|
||||
func slicebytetostring(*[32]byte, []byte) string
|
||||
func slicebytetostringtmp([]byte) string
|
||||
func slicerunetostring([]rune) string
|
||||
func stringtoslicebyte(string) []byte
|
||||
func slicerunetostring(*[32]byte, []rune) string
|
||||
func stringtoslicebyte(*[32]byte, string) []byte
|
||||
func stringtoslicebytetmp(string) []byte
|
||||
func stringtoslicerune(string) []rune
|
||||
func stringtoslicerune(*[32]rune, string) []rune
|
||||
func stringiter(string, int) int
|
||||
func stringiter2(string, int) (retk int, retv rune)
|
||||
func slicecopy(to any, fr any, wid uintptr) int
|
||||
|
@ -1398,13 +1398,25 @@ walkexpr(Node **np, NodeList **init)
|
||||
goto ret;
|
||||
|
||||
case OARRAYRUNESTR:
|
||||
// slicerunetostring([]rune) string;
|
||||
n = mkcall("slicerunetostring", n->type, init, n->left);
|
||||
// slicerunetostring(*[32]byte, []rune) string;
|
||||
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;
|
||||
|
||||
case OSTRARRAYBYTE:
|
||||
// stringtoslicebyte(string) []byte;
|
||||
n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING]));
|
||||
// stringtoslicebyte(*32[byte], string) []byte;
|
||||
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;
|
||||
|
||||
case OSTRARRAYBYTETMP:
|
||||
@ -1413,8 +1425,14 @@ walkexpr(Node **np, NodeList **init)
|
||||
goto ret;
|
||||
|
||||
case OSTRARRAYRUNE:
|
||||
// stringtoslicerune(string) []rune
|
||||
n = mkcall("stringtoslicerune", n->type, init, n->left);
|
||||
// stringtoslicerune(*[32]rune, string) []rune
|
||||
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;
|
||||
|
||||
case OCMPIFACE:
|
||||
|
@ -129,8 +129,13 @@ func slicebytetostringtmp(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
func stringtoslicebyte(s string) []byte {
|
||||
b := rawbyteslice(len(s))
|
||||
func stringtoslicebyte(buf *tmpBuf, s string) []byte {
|
||||
var b []byte
|
||||
if buf != nil && len(s) <= len(buf) {
|
||||
b = buf[:len(s)]
|
||||
} else {
|
||||
b = rawbyteslice(len(s))
|
||||
}
|
||||
copy(b, s)
|
||||
return b
|
||||
}
|
||||
@ -147,7 +152,7 @@ func stringtoslicebytetmp(s string) []byte {
|
||||
return *(*[]byte)(unsafe.Pointer(&ret))
|
||||
}
|
||||
|
||||
func stringtoslicerune(s string) []rune {
|
||||
func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune {
|
||||
// two passes.
|
||||
// unlike slicerunetostring, no race because strings are immutable.
|
||||
n := 0
|
||||
@ -157,7 +162,12 @@ func stringtoslicerune(s string) []rune {
|
||||
s = s[k:]
|
||||
n++
|
||||
}
|
||||
a := rawruneslice(n)
|
||||
var a []rune
|
||||
if buf != nil && n <= len(buf) {
|
||||
a = buf[:n]
|
||||
} else {
|
||||
a = rawruneslice(n)
|
||||
}
|
||||
n = 0
|
||||
for len(t) > 0 {
|
||||
r, k := charntorune(t)
|
||||
@ -168,7 +178,7 @@ func stringtoslicerune(s string) []rune {
|
||||
return a
|
||||
}
|
||||
|
||||
func slicerunetostring(a []rune) string {
|
||||
func slicerunetostring(buf *tmpBuf, a []rune) string {
|
||||
if raceenabled && len(a) > 0 {
|
||||
racereadrangepc(unsafe.Pointer(&a[0]),
|
||||
uintptr(len(a))*unsafe.Sizeof(a[0]),
|
||||
@ -180,7 +190,7 @@ func slicerunetostring(a []rune) string {
|
||||
for _, r := range a {
|
||||
size1 += runetochar(dum[:], r)
|
||||
}
|
||||
s, b := rawstring(size1 + 3)
|
||||
s, b := rawstringtmp(buf, size1+3)
|
||||
size2 := 0
|
||||
for _, r := range a {
|
||||
// check for race
|
||||
@ -309,11 +319,6 @@ func gobytes(p *byte, n int) []byte {
|
||||
return x
|
||||
}
|
||||
|
||||
func gostringsize(n int) string {
|
||||
s, _ := rawstring(n)
|
||||
return s
|
||||
}
|
||||
|
||||
func gostring(p *byte) string {
|
||||
l := findnull(p)
|
||||
if l == 0 {
|
||||
|
@ -212,7 +212,7 @@ func foo21() func() int {
|
||||
func foo21a() func() int {
|
||||
x := 42 // ERROR "moved to heap: x"
|
||||
return func() int { // ERROR "func literal escapes to heap"
|
||||
x++ // ERROR "&x escapes to heap"
|
||||
x++ // ERROR "&x escapes to heap"
|
||||
return x
|
||||
}
|
||||
}
|
||||
@ -1560,14 +1560,14 @@ func ptrlitNoescape() {
|
||||
|
||||
func ptrlitNoEscape2() {
|
||||
// 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"
|
||||
sink = *x
|
||||
}
|
||||
|
||||
func ptrlitEscape() {
|
||||
// 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"
|
||||
sink = x
|
||||
}
|
||||
@ -1619,7 +1619,7 @@ type StructWithString struct {
|
||||
// to just x, and thus &i looks escaping.
|
||||
func fieldFlowTracking() {
|
||||
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"
|
||||
sink = x.s
|
||||
}
|
||||
@ -1703,3 +1703,51 @@ func intstring2() {
|
||||
s := string(x) // ERROR "string\(x\) escapes to heap" "moved to heap: s"
|
||||
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 {
|
||||
func foo21a() func() int {
|
||||
x := 42 // ERROR "moved to heap: x"
|
||||
return func() int { // ERROR "func literal escapes to heap"
|
||||
x++ // ERROR "&x escapes to heap"
|
||||
x++ // ERROR "&x escapes to heap"
|
||||
return x
|
||||
}
|
||||
}
|
||||
@ -1560,14 +1560,14 @@ func ptrlitNoescape() {
|
||||
|
||||
func ptrlitNoEscape2() {
|
||||
// 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"
|
||||
sink = *x
|
||||
}
|
||||
|
||||
func ptrlitEscape() {
|
||||
// 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"
|
||||
sink = x
|
||||
}
|
||||
@ -1619,7 +1619,7 @@ type StructWithString struct {
|
||||
// to just x, and thus &i looks escaping.
|
||||
func fieldFlowTracking() {
|
||||
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"
|
||||
sink = x.s
|
||||
}
|
||||
@ -1703,3 +1703,51 @@ func intstring2() {
|
||||
s := string(x) // ERROR "string\(x\) escapes to heap" "moved to heap: s"
|
||||
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
Block a user