]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: enable zero-copy string->[]byte conversions
authorMatthew Dempsky <mdempsky@google.com>
Thu, 17 Aug 2023 05:57:12 +0000 (22:57 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 17 Aug 2023 19:37:36 +0000 (19:37 +0000)
This CL enables the latent support for string->[]byte conversions
added go.dev/cl/520259.

One catch is that we need to make sure []byte("") evaluates to a
non-nil slice, even if "" is (nil, 0). This CL addresses that by
adding a "ptr != nil" check for OSTR2BYTESTMP, unless the NonNil flag
is set.

The existing uses of OSTR2BYTESTMP (which aren't concerned about
[]byte("") evaluating to nil) are updated to set this flag.

Fixes #2205.

Change-Id: I35a9cb16c164cd86156b7560915aba5108d8b523
Reviewed-on: https://go-review.googlesource.com/c/go/+/520395
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
src/cmd/compile/internal/escape/escape.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/compile/internal/walk/order.go
src/cmd/compile/internal/walk/switch.go
test/escape2.go
test/escape2n.go

index 2882f9fda3e1e580ae91e878f0c26e568d187454..5f5dab31f7d95e53cf9ccbdd1d60c75361000c19 100644 (file)
@@ -345,16 +345,11 @@ func (b *batch) finish(fns []*ir.Func) {
 
                // If the result of a string->[]byte conversion is never mutated,
                // then it can simply reuse the string's memory directly.
-               //
-               // TODO(mdempsky): Enable in a subsequent CL. We need to ensure
-               // []byte("") evaluates to []byte{}, not []byte(nil).
-               if false {
-                       if n, ok := n.(*ir.ConvExpr); ok && n.Op() == ir.OSTR2BYTES && !loc.hasAttr(attrMutates) {
-                               if base.Flag.LowerM >= 1 {
-                                       base.WarnfAt(n.Pos(), "zero-copy string->[]byte conversion")
-                               }
-                               n.SetOp(ir.OSTR2BYTESTMP)
+               if n, ok := n.(*ir.ConvExpr); ok && n.Op() == ir.OSTR2BYTES && !loc.hasAttr(attrMutates) {
+                       if base.Flag.LowerM >= 1 {
+                               base.WarnfAt(n.Pos(), "zero-copy string->[]byte conversion")
                        }
+                       n.SetOp(ir.OSTR2BYTESTMP)
                }
        }
 }
index 25e93b531d55ebe649192633404fc57a2d13c758..c3a47ac1d02a006ee871645a4a072fd56b1b877e 100644 (file)
@@ -2659,6 +2659,14 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value {
                n := n.(*ir.ConvExpr)
                str := s.expr(n.X)
                ptr := s.newValue1(ssa.OpStringPtr, s.f.Config.Types.BytePtr, str)
+               if !n.NonNil() {
+                       // We need to ensure []byte("") evaluates to []byte{}, and not []byte(nil).
+                       //
+                       // TODO(mdempsky): Investigate using "len != 0" instead of "ptr != nil".
+                       cond := s.newValue2(ssa.OpNeqPtr, types.Types[types.TBOOL], ptr, s.constNil(ptr.Type))
+                       zerobase := s.newValue1A(ssa.OpAddr, ptr.Type, ir.Syms.Zerobase, s.sb)
+                       ptr = s.ternary(cond, ptr, zerobase)
+               }
                len := s.newValue1(ssa.OpStringLen, types.Types[types.TINT], str)
                return s.newValue3(ssa.OpSliceMake, n.Type(), ptr, len, len)
        case ir.OCFUNC:
index 3e3bda15e7bd04a26cb747ecefb71618ef68fc4e..c38477f33e05f9c97ed4626035454f85cda6a22f 100644 (file)
@@ -815,8 +815,14 @@ func (o *orderState) stmt(n ir.Node) {
                // Mark []byte(str) range expression to reuse string backing storage.
                // It is safe because the storage cannot be mutated.
                n := n.(*ir.RangeStmt)
-               if n.X.Op() == ir.OSTR2BYTES {
-                       n.X.(*ir.ConvExpr).SetOp(ir.OSTR2BYTESTMP)
+               if x, ok := n.X.(*ir.ConvExpr); ok {
+                       switch x.Op() {
+                       case ir.OSTR2BYTES:
+                               x.SetOp(ir.OSTR2BYTESTMP)
+                               fallthrough
+                       case ir.OSTR2BYTESTMP:
+                               x.MarkNonNil() // "range []byte(nil)" is fine
+                       }
                }
 
                t := o.markTemp()
index 3af457b8c002e2badd391df8a928b970a12d2df5..f59ae33f51afc38dba986669e00c4411090e20ab 100644 (file)
@@ -736,6 +736,7 @@ func stringSearch(expr ir.Node, cc []exprClause, out *ir.Nodes) {
        // Convert expr to a []int8
        slice := ir.NewConvExpr(base.Pos, ir.OSTR2BYTESTMP, types.NewSlice(types.Types[types.TINT8]), expr)
        slice.SetTypecheck(1) // legacy typechecker doesn't handle this op
+       slice.MarkNonNil()
        // Load the byte we're splitting on.
        load := ir.NewIndexExpr(base.Pos, slice, ir.NewInt(base.Pos, int64(bestIdx)))
        // Compare with the value we're splitting on.
index e3e5904cde525bc9b4c2412a1c52b5764e23f837..99f85914a32ed7caa1d3addaf444ff45e4b63ddf 100644 (file)
@@ -1729,7 +1729,7 @@ func intstring2() {
 
 func stringtoslicebyte0() {
        s := "foo"
-       x := []byte(s) // ERROR "\(\[\]byte\)\(s\) does not escape$"
+       x := []byte(s) // ERROR "\(\[\]byte\)\(s\) does not escape$" "zero-copy string->\[\]byte conversion"
        _ = x
 }
 
index 57cc1a01639eb410656caaba1e6bc092c4b59d0e..350be65202c00b14b24ce9ad258842554a1d9268 100644 (file)
@@ -1729,7 +1729,7 @@ func intstring2() {
 
 func stringtoslicebyte0() {
        s := "foo"
-       x := []byte(s) // ERROR "\(\[\]byte\)\(s\) does not escape$"
+       x := []byte(s) // ERROR "\(\[\]byte\)\(s\) does not escape$" "zero-copy string->\[\]byte conversion"
        _ = x
 }