]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/escape: additional constant and zero value tests and logging
authorthepudds <thepudds1460@gmail.com>
Wed, 12 Feb 2025 23:39:08 +0000 (18:39 -0500)
committerGopher Robot <gobot@golang.org>
Tue, 20 May 2025 16:19:42 +0000 (09:19 -0700)
This adds additional logging for the work that walk does to reduce
how often an interface conversion results in an allocation.

Also, as part of #71359, we will be updating how escape analysis and
walk handle basic literals, composite literals, and zero values,
so add some tests that uses this new logging.

By the end of our CL stack, we address all of these tests.

Updates #71359

Change-Id: I43fde8343d9aacaec1e05360417908014a86c8bd
Reviewed-on: https://go-review.googlesource.com/c/go/+/649076
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/compile/internal/base/debug.go
src/cmd/compile/internal/walk/convert.go
test/escape_iface_data.go [new file with mode: 0644]

index 7bcbcb3e2c0cb69955b8c62916067420868651ae..10393e773c366dceda48afada81600b3aac440c6 100644 (file)
@@ -29,6 +29,7 @@ type DebugFlags struct {
        DumpPtrs              int    `help:"show Node pointers values in dump output"`
        DwarfInl              int    `help:"print information about DWARF inlined function creation"`
        EscapeMutationsCalls  int    `help:"print extra escape analysis diagnostics about mutations and calls" concurrent:"ok"`
+       EscapeDebug           int    `help:"print information about escape analysis and resulting optimizations" concurrent:"ok"`
        Export                int    `help:"print export data"`
        FIPSHash              string `help:"hash value for FIPS debugging" concurrent:"ok"`
        Fmahash               string `help:"hash value for use in debugging platform-dependent multiply-add use" concurrent:"ok"`
index fc1e4c84e781531fd52dbfc7612aae8510414ce5..4c443f71b939a244b3a7d3f666a9aa43a3a16cd4 100644 (file)
@@ -141,16 +141,27 @@ func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node {
                isInteger = sc.IsInteger()
                isBool = sc.IsBoolean()
        }
+
+       diagnose := func(msg string, n ir.Node) {
+               if base.Debug.EscapeDebug > 0 {
+                       // This output is most useful with -gcflags=-W=2 or similar because
+                       // it often prints a temp variable name.
+                       base.WarnfAt(n.Pos(), "convert: %s: %v", msg, n)
+               }
+       }
+
        // Try a bunch of cases to avoid an allocation.
        var value ir.Node
        switch {
        case fromType.Size() == 0:
                // n is zero-sized. Use zerobase.
+               diagnose("using global for zero-sized interface value", n)
                cheapExpr(n, init) // Evaluate n for side-effects. See issue 19246.
                value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
        case isBool || fromType.Size() == 1 && isInteger:
                // n is a bool/byte. Use staticuint64s[n * 8] on little-endian
                // and staticuint64s[n * 8 + 7] on big-endian.
+               diagnose("using global for single-byte interface value", n)
                n = cheapExpr(n, init)
                n = soleComponent(init, n)
                // byteindex widens n so that the multiplication doesn't overflow.
@@ -166,9 +177,11 @@ func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node {
                value = xe
        case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly():
                // n is a readonly global; use it directly.
+               diagnose("using global for interface value", n)
                value = n
        case conv.Esc() == ir.EscNone && fromType.Size() <= 1024:
                // n does not escape. Use a stack temporary initialized to n.
+               diagnose("using stack temporary for interface value", n)
                value = typecheck.TempAt(base.Pos, ir.CurFunc, fromType)
                init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n)))
        }
diff --git a/test/escape_iface_data.go b/test/escape_iface_data.go
new file mode 100644 (file)
index 0000000..556be20
--- /dev/null
@@ -0,0 +1,257 @@
+// errorcheck -0 -d=escapedebug=1
+
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test the data word used for interface conversions
+// that might otherwise allocate.
+
+package dataword
+
+var sink interface{}
+
+func string1() {
+       sink = "abc" // ERROR "using global for interface value"
+}
+
+func string2() {
+       v := "abc"
+       sink = v
+}
+
+func string3() {
+       sink = "" // ERROR "using global for interface value"
+}
+
+func string4() {
+       v := ""
+       sink = v
+}
+
+func string5() {
+       var a any = "abc" // ERROR "using global for interface value"
+       _ = a
+}
+
+func string6() {
+       var a any
+       v := "abc" // ERROR "using stack temporary for interface value"
+       a = v
+       _ = a
+}
+
+// string7 can be inlined.
+func string7(v string) {
+       sink = v
+}
+
+func string8() {
+       v0 := "abc"
+       v := v0
+       string7(v)
+}
+
+func string9() {
+       v0 := "abc"
+       v := v0
+       f := func() {
+               string7(v)
+       }
+       f()
+}
+
+func string10() {
+       v0 := "abc"
+       v := v0
+       f := func() {
+               f2 := func() {
+                       string7(v)
+               }
+               f2()
+       }
+       f()
+}
+
+func string11() {
+       v0 := "abc"
+       v := v0
+       defer func() {
+               string7(v)
+       }()
+}
+
+func integer1() {
+       sink = 42 // ERROR "using global for interface value"
+}
+
+func integer2() {
+       v := 42
+       sink = v
+}
+
+func integer3() {
+       sink = 0 // ERROR "using global for interface value"
+}
+
+func integer4a() {
+       v := 0
+       sink = v
+}
+
+func integer4b() {
+       v := uint8(0)
+       sink = v // ERROR "using global for single-byte interface value"
+}
+
+func integer5() {
+       var a any = 42 // ERROR "using global for interface value"
+       _ = a
+}
+
+func integer6() {
+       var a any
+       v := 42 // ERROR "using stack temporary for interface value"
+       a = v
+       _ = a
+}
+
+func integer7(v int) {
+       sink = v
+}
+
+type M interface{ M() }
+
+type MyInt int
+
+func (m MyInt) M() {}
+
+func escapes(m M) {
+       sink = m
+}
+
+func named1a() {
+       sink = MyInt(42) // ERROR "using global for interface value"
+}
+
+func named1b() {
+       escapes(MyInt(42)) // ERROR "using global for interface value"
+}
+
+func named2a() {
+       v := MyInt(0)
+       sink = v
+}
+
+func named2b() {
+       v := MyInt(42)
+       escapes(v)
+}
+
+// Unfortunate: we currently require matching types, which we could relax.
+func named2c() {
+       v := 42
+       sink = MyInt(v)
+}
+
+// Unfortunate: we currently require matching types, which we could relax.
+func named2d() {
+       v := 42
+       escapes(MyInt(v))
+}
+func named3a() {
+       sink = MyInt(42) // ERROR "using global for interface value"
+}
+
+func named3b() {
+       escapes(MyInt(0)) // ERROR "using global for interface value"
+}
+
+func named4a() {
+       v := MyInt(0)
+       sink = v
+}
+
+func named4b() {
+       v := MyInt(0)
+       escapes(v)
+}
+
+func named4c() {
+       v := 0
+       sink = MyInt(v)
+}
+
+func named4d() {
+       v := 0
+       escapes(MyInt(v))
+}
+
+func named5() {
+       var a any = MyInt(42) // ERROR "using global for interface value"
+       _ = a
+}
+
+func named6() {
+       var a any
+       v := MyInt(42) // ERROR "using stack temporary for interface value"
+       a = v
+       _ = a
+}
+
+func named7a(v MyInt) {
+       sink = v
+}
+
+func named7b(v MyInt) {
+       escapes(v)
+}
+
+type S struct{ a, b int64 }
+
+func struct1() {
+       sink = S{1, 1}
+}
+
+func struct2() {
+       v := S{1, 1}
+       sink = v
+}
+
+func struct3() {
+       sink = S{}
+}
+
+func struct4() {
+       v := S{}
+       sink = v
+}
+
+func struct5() {
+       var a any = S{1, 1} // ERROR "using stack temporary for interface value"
+       _ = a
+}
+
+func struct6() {
+       var a any
+       v := S{1, 1}
+       a = v // ERROR "using stack temporary for interface value"
+       _ = a
+}
+
+func struct7(v S) {
+       sink = v
+}
+
+func emptyStruct1() {
+       sink = struct{}{} // ERROR "using global for zero-sized interface value"
+}
+
+func emptyStruct2() {
+       v := struct{}{}
+       sink = v // ERROR "using global for zero-sized interface value"
+}
+
+func emptyStruct3(v struct{}) { // ERROR "using global for zero-sized interface value"
+       sink = v
+}