mirror of
https://github.com/golang/go
synced 2024-11-12 05:30:21 -07:00
cmd/compile: make []byte("...") more efficient
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>
This commit is contained in:
parent
05581fcc5c
commit
ceb0c371d9
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -816,7 +816,7 @@
|
||||
// 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]))
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user