]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/gc: allocate buffers for non-escaping string conversions on stack
authorDmitry Vyukov <dvyukov@google.com>
Fri, 30 Jan 2015 06:14:13 +0000 (09:14 +0300)
committerDmitry Vyukov <dvyukov@google.com>
Thu, 12 Feb 2015 08:29:53 +0000 (08:29 +0000)
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>
src/cmd/gc/builtin.c
src/cmd/gc/esc.c
src/cmd/gc/runtime.go
src/cmd/gc/walk.c
src/runtime/string.go
test/escape2.go
test/escape2n.go

index f154ae70b1c5bfe8f9627a1355db710e63f648ef..cdcc8e7cbc45015a286dbe744abd3cf263855d25 100644 (file)
@@ -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"
index 4f77983926613d577e55c17ebc3cd5b4e4d5b1c4..5b09c0b7fb05727b1df2d06102ab73dc62568361 100644 (file)
@@ -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:
index 80550f856dd71b5515ef18a4162953a072a884e3..8648a973e81a3841911d3a50f951b46836cf61af 100644 (file)
@@ -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
index 07df5a1adc9bb0ffd2254f63baa8a3c22c89da6b..91568371d7a6d037c87ef83c9de16f6ef1c4cca9 100644 (file)
@@ -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:
index 46c3502f7778c6a8b2e93433df89dd6ec439fb38..0ba309cf0212a78c738c348f170f185d076342b3 100644 (file)
@@ -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 {
index 8c50277e9dc730d523569c237084e30e3f418866..947dcc951525a0c628c9c19a33ffc740e05fe3b3 100644 (file)
@@ -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"
+}
index 31f4ed083c469147e834dec21522337cc7163c45..d9d95e81dc019276353f8bfa39a530fc596b27e5 100644 (file)
@@ -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"
+}