]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: make []byte("...") more efficient
authorKeith Randall <khr@google.com>
Tue, 9 Oct 2018 00:46:45 +0000 (17:46 -0700)
committerKeith Randall <khr@golang.org>
Wed, 10 Oct 2018 16:10:40 +0000 (16:10 +0000)
Do []byte(string) conversions more efficiently when the string
is a constant. Instead of calling stringtobyteslice, allocate
just the space we need and encode the initialization directly.

[]byte("foo") rewrites to the following pseudocode:

var s [3]byte // on heap or stack, depending on whether b escapes
s = *(*[3]byte)(&"foo"[0]) // initialize s from the string
b = s[:]

which generates this assembly:

0x001d 00029 (tmp1.go:9) LEAQ type.[3]uint8(SB), AX
0x0024 00036 (tmp1.go:9) MOVQ AX, (SP)
0x0028 00040 (tmp1.go:9) CALL runtime.newobject(SB)
0x002d 00045 (tmp1.go:9) MOVQ 8(SP), AX
0x0032 00050 (tmp1.go:9) MOVBLZX go.string."foo"+2(SB), CX
0x0039 00057 (tmp1.go:9) MOVWLZX go.string."foo"(SB), DX
0x0040 00064 (tmp1.go:9) MOVW DX, (AX)
0x0043 00067 (tmp1.go:9) MOVB CL, 2(AX)
// Then the slice is b = {AX, 3, 3}

The generated code is still not optimal, as it still does load/store
from read-only memory instead of constant stores.  Next CL...

Update #26498
Fixes #10170

Change-Id: I4b990b19f9a308f60c8f4f148934acffefe0a5bd
Reviewed-on: https://go-review.googlesource.com/c/140698
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/cmd/compile/internal/gc/esc.go
src/cmd/compile/internal/gc/go.go
src/cmd/compile/internal/gc/walk.go
src/cmd/compile/internal/ssa/gen/generic.rules
src/cmd/compile/internal/ssa/rewritegeneric.go
test/codegen/strings.go

index eba66d9c67b7fd78e1ee4583b3809bce8cce6dda..5beb43d5489ac9470e7a3ad249f845da04efeb00 100644 (file)
@@ -798,9 +798,8 @@ func (e *EscState) esc(n *Node, parent *Node) {
        // gathered here.
        if n.Esc != EscHeap && n.Type != nil &&
                (n.Type.Width > maxStackVarSize ||
-                       (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= 1<<16 ||
+                       (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize ||
                        n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
-
                // isSmallMakeSlice returns false for non-constant len/cap.
                // If that's the case, print a more accurate escape reason.
                var msgVerb, escapeMsg string
index d8ab5eb39cc6ba3022add4a872cbde3c0aaa11fc..605afd6407979002d34b693e07eaa812b52fc4a1 100644 (file)
@@ -13,8 +13,18 @@ import (
 )
 
 const (
-       BADWIDTH        = types.BADWIDTH
+       BADWIDTH = types.BADWIDTH
+
+       // maximum size variable which we will allocate on the stack.
+       // This limit is for explicit variable declarations like "var x T" or "x := ...".
        maxStackVarSize = 10 * 1024 * 1024
+
+       // maximum size of implicit variables that we will allocate on the stack.
+       //   p := new(T)          allocating T on the stack
+       //   p := &T{}            allocating T on the stack
+       //   s := make([]T, n)    allocating [n]T on the stack
+       //   s := []byte("...")   allocating [n]byte on the stack
+       maxImplicitStackVarSize = 64 * 1024
 )
 
 // isRuntimePkg reports whether p is package runtime.
index 80fdc55b5df47733e45437d951535494d51b5cd1..c3201c140494be54ccadd57f0b868b0df047378c 100644 (file)
@@ -367,7 +367,7 @@ func isSmallMakeSlice(n *Node) bool {
        }
        t := n.Type
 
-       return smallintconst(l) && smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < (1<<16)/t.Elem().Width)
+       return smallintconst(l) && smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < maxImplicitStackVarSize/t.Elem().Width)
 }
 
 // walk the whole tree of the body of an
@@ -1204,7 +1204,7 @@ opswitch:
 
        case ONEW:
                if n.Esc == EscNone {
-                       if n.Type.Elem().Width >= 1<<16 {
+                       if n.Type.Elem().Width >= maxImplicitStackVarSize {
                                Fatalf("large ONEW with EscNone: %v", n)
                        }
                        r := temp(n.Type.Elem())
@@ -1593,8 +1593,36 @@ opswitch:
 
                n = mkcall("slicerunetostring", n.Type, init, a, n.Left)
 
-               // stringtoslicebyte(*32[byte], string) []byte;
        case OSTRARRAYBYTE:
+               s := n.Left
+               if Isconst(s, CTSTR) {
+                       sc := s.Val().U.(string)
+
+                       // Allocate a [n]byte of the right size.
+                       t := types.NewArray(types.Types[TUINT8], int64(len(sc)))
+                       var a *Node
+                       if n.Esc == EscNone && len(sc) <= maxImplicitStackVarSize {
+                               a = nod(OADDR, temp(t), nil)
+                       } else {
+                               a = callnew(t)
+                       }
+                       p := temp(t.PtrTo()) // *[n]byte
+                       init.Append(typecheck(nod(OAS, p, a), Etop))
+
+                       // Copy from the static string data to the [n]byte.
+                       if len(sc) > 0 {
+                               as := nod(OAS,
+                                       nod(OIND, p, nil),
+                                       nod(OIND, convnop(nod(OSPTR, s, nil), t.PtrTo()), nil))
+                               init.Append(typecheck(as, Etop))
+                       }
+
+                       // Slice the [n]byte to a []byte.
+                       n.Op = OSLICEARR
+                       n.Left = p
+                       n = walkexpr(n, init)
+                       break
+               }
                a := nodnil()
 
                if n.Esc == EscNone {
@@ -1604,7 +1632,8 @@ opswitch:
                        a = nod(OADDR, temp(t), nil)
                }
 
-               n = mkcall("stringtoslicebyte", n.Type, init, a, conv(n.Left, types.Types[TSTRING]))
+               // stringtoslicebyte(*32[byte], string) []byte;
+               n = mkcall("stringtoslicebyte", n.Type, init, a, conv(s, types.Types[TSTRING]))
 
        case OSTRARRAYBYTETMP:
                // []byte(string) conversion that creates a slice
index d490e32f3d32478c2f3a4881e1f4487963d819ac..8d2691d29cb829dfde326f12cc4b728fd7d5254b 100644 (file)
 // Decomposing StringMake and lowering of StringPtr and StringLen
 // happens in a later pass, dec, so that these operations are available
 // to other passes for optimizations.
-(StringPtr (StringMake (Const64 <t> [c]) _)) -> (Const64 <t> [c])
+(StringPtr (StringMake (Addr <t> {s} base) _)) -> (Addr <t> {s} base)
 (StringLen (StringMake _ (Const64 <t> [c]))) -> (Const64 <t> [c])
 (ConstString {s}) && config.PtrSize == 4 && s.(string) == "" ->
   (StringMake (ConstNil) (Const32 <typ.Int> [0]))
index 2f239faa4903fd410893f53aef76420a6a4e5e61..26341a9217aa5e0286132eb22a08e721a3eaa63e 100644 (file)
@@ -28748,9 +28748,9 @@ func rewriteValuegeneric_OpStringLen_0(v *Value) bool {
        return false
 }
 func rewriteValuegeneric_OpStringPtr_0(v *Value) bool {
-       // match: (StringPtr (StringMake (Const64 <t> [c]) _))
+       // match: (StringPtr (StringMake (Addr <t> {s} base) _))
        // cond:
-       // result: (Const64 <t> [c])
+       // result: (Addr <t> {s} base)
        for {
                v_0 := v.Args[0]
                if v_0.Op != OpStringMake {
@@ -28758,14 +28758,16 @@ func rewriteValuegeneric_OpStringPtr_0(v *Value) bool {
                }
                _ = v_0.Args[1]
                v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpConst64 {
+               if v_0_0.Op != OpAddr {
                        break
                }
                t := v_0_0.Type
-               c := v_0_0.AuxInt
-               v.reset(OpConst64)
+               s := v_0_0.Aux
+               base := v_0_0.Args[0]
+               v.reset(OpAddr)
                v.Type = t
-               v.AuxInt = c
+               v.Aux = s
+               v.AddArg(base)
                return true
        }
        return false
index ccb6bd4273a1fbae8b800382a75c675100d46d7d..f4adfac0cc725dce1f45055b9ee9179cf2fbadfb 100644 (file)
@@ -13,3 +13,10 @@ func CountRunes(s string) int { // Issue #24923
        // amd64:`.*countrunes`
        return len([]rune(s))
 }
+
+func ToByteSlice() []byte { // Issue #24698
+       // amd64:`LEAQ\ttype\.\[3\]uint8`
+       // amd64:`CALL\truntime\.newobject`
+       // amd64:-`.*runtime.stringtoslicebyte`
+       return []byte("foo")
+}