go/test/escape_iface_data.go

298 lines
4.9 KiB
Go
Raw Normal View History

// 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"
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
sink = v // ERROR "using global for interface value"
}
func string3() {
sink = "" // ERROR "using global for interface value"
}
func string4() {
v := ""
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
sink = v // ERROR "using global for interface value"
}
func string5() {
var a any = "abc" // ERROR "using global for interface value"
_ = a
}
func string6() {
var a any
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
v := "abc"
a = v // ERROR "using global for interface value"
_ = a
}
// string7 can be inlined.
func string7(v string) {
sink = v
}
func string8() {
v0 := "abc"
v := v0
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
string7(v) // ERROR "using global for interface value"
}
func string9() {
v0 := "abc"
v := v0
f := func() {
string7(v)
}
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
f() // ERROR "using global for interface value"
}
func string10() {
v0 := "abc"
v := v0
f := func() {
f2 := func() {
string7(v)
}
f2()
}
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
f() // ERROR "using global for interface value"
}
func string11() {
v0 := "abc"
v := v0
defer func() {
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
string7(v) // ERROR "using global for interface value"
}()
}
func integer1() {
sink = 42 // ERROR "using global for interface value"
}
func integer2() {
v := 42
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
sink = v // ERROR "using global for interface value"
}
func integer3() {
sink = 0 // ERROR "using global for interface value"
}
func integer4a() {
v := 0
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
sink = v // ERROR "using global for interface value"
}
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
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
v := 42
a = v // ERROR "using global for interface value"
_ = 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)
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
sink = v // ERROR "using global for interface value"
}
func named2b() {
v := MyInt(42)
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
escapes(v) // ERROR "using global for interface value"
}
func named2c() {
v := 42
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
sink = MyInt(v) // ERROR "using global for interface value"
}
func named2d() {
v := 42
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
escapes(MyInt(v)) // ERROR "using global for interface value"
}
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)
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
sink = v // ERROR "using global for interface value"
}
func named4b() {
v := MyInt(0)
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
escapes(v) // ERROR "using global for interface value"
}
func named4c() {
v := 0
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
sink = MyInt(v) // ERROR "using global for interface value"
}
func named4d() {
v := 0
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
escapes(MyInt(v)) // ERROR "using global for interface value"
}
func named5() {
var a any = MyInt(42) // ERROR "using global for interface value"
_ = a
}
func named6() {
var a any
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
v := MyInt(42)
a = v // ERROR "using global for interface value"
_ = a
}
func named7a(v MyInt) {
sink = v
}
func named7b(v MyInt) {
escapes(v)
}
type S struct{ a, b int64 }
func struct1() {
cmd/compile/internal/walk: convert composite literals to interfaces without allocating Today, this interface conversion causes the struct literal to be heap allocated: var sink any func example1() { sink = S{1, 1} } For basic literals like integers that are directly used in an interface conversion that would otherwise allocate, the compiler is able to use read-only global storage (see #18704). This CL extends that to struct and array literals as well by creating read-only global storage that is able to represent for example S{1, 1}, and then using a pointer to that storage in the interface when the interface conversion happens. A more challenging example is: func example2() { v := S{1, 1} sink = v } In this case, the struct literal is not directly part of the interface conversion, but is instead assigned to a local variable. To still avoid heap allocation in cases like this, in walk we construct a cache that maps from expressions used in interface conversions to earlier expressions that can be used to represent the same value (via ir.ReassignOracle.StaticValue). This is somewhat analogous to how we avoided heap allocation for basic literals in CL 649077 earlier in our stack, though here we also need to do a little more work to create the read-only global. CL 649076 (also earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. See the writeup in #71359 for details. Fixes #71359 Fixes #71323 Updates #62653 Updates #53465 Updates #8618 Change-Id: I8924f0c69ff738ea33439bd6af7b4066af493b90 Reviewed-on: https://go-review.googlesource.com/c/go/+/649555 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-13 23:49:50 -05:00
sink = S{1, 1} // ERROR "using global for interface value"
}
func struct2() {
v := S{1, 1}
cmd/compile/internal/walk: convert composite literals to interfaces without allocating Today, this interface conversion causes the struct literal to be heap allocated: var sink any func example1() { sink = S{1, 1} } For basic literals like integers that are directly used in an interface conversion that would otherwise allocate, the compiler is able to use read-only global storage (see #18704). This CL extends that to struct and array literals as well by creating read-only global storage that is able to represent for example S{1, 1}, and then using a pointer to that storage in the interface when the interface conversion happens. A more challenging example is: func example2() { v := S{1, 1} sink = v } In this case, the struct literal is not directly part of the interface conversion, but is instead assigned to a local variable. To still avoid heap allocation in cases like this, in walk we construct a cache that maps from expressions used in interface conversions to earlier expressions that can be used to represent the same value (via ir.ReassignOracle.StaticValue). This is somewhat analogous to how we avoided heap allocation for basic literals in CL 649077 earlier in our stack, though here we also need to do a little more work to create the read-only global. CL 649076 (also earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. See the writeup in #71359 for details. Fixes #71359 Fixes #71323 Updates #62653 Updates #53465 Updates #8618 Change-Id: I8924f0c69ff738ea33439bd6af7b4066af493b90 Reviewed-on: https://go-review.googlesource.com/c/go/+/649555 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-13 23:49:50 -05:00
sink = v // ERROR "using global for interface value"
}
func struct3() {
cmd/compile/internal/walk: use global zeroVal in interface conversions for zero values This is a small-ish adjustment to the change earlier in our stack in CL 649555, which started creating read-only global storage for a composite literal used in an interface conversion and setting the interface data pointer to point to that global storage. In some cases, there are execution-time performance benefits to point to runtime.zeroVal in particular. In reflect, pointer checks against the runtime.zeroVal memory address are used to side-step some work, such as in reflect.Value.Set and reflect.Value.IsZero. In this CL, we therefore dig up the zeroVal symbol, and we use the machinery from earlier in our stack to use a pointer to zeroVal for the interface data pointer if we see examples like: sink = S{} or: s := S{} sink = s CL 649076 (also earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. We add a benchmark in reflect to show examples of performance benefit. The left column is our immediately prior CL 649555, and the right is this CL. (The arrays of structs here do not seem to benefit, which we attempt to address in our next CL). goos: linux goarch: amd64 pkg: reflect cpu: Intel(R) Xeon(R) CPU @ 2.80GHz │ cl-649555 │ new │ │ sec/op │ sec/op vs base │ Zero/IsZero/ByteArray/size=16-4 4.176n ± 0% 4.171n ± 0% ~ (p=0.151 n=20) Zero/IsZero/ByteArray/size=64-4 6.921n ± 0% 3.864n ± 0% -44.16% (p=0.000 n=20) Zero/IsZero/ByteArray/size=1024-4 21.210n ± 0% 3.878n ± 0% -81.72% (p=0.000 n=20) Zero/IsZero/BigStruct/size=1024-4 25.505n ± 0% 5.061n ± 0% -80.15% (p=0.000 n=20) Zero/IsZero/SmallStruct/size=16-4 4.188n ± 0% 4.191n ± 0% ~ (p=0.106 n=20) Zero/IsZero/SmallStructArray/size=64-4 8.639n ± 0% 8.636n ± 0% ~ (p=0.973 n=20) Zero/IsZero/SmallStructArray/size=1024-4 79.99n ± 0% 80.06n ± 0% ~ (p=0.213 n=20) Zero/IsZero/Time/size=24-4 7.232n ± 0% 3.865n ± 0% -46.56% (p=0.000 n=20) Zero/SetZero/ByteArray/size=16-4 13.47n ± 0% 13.09n ± 0% -2.78% (p=0.000 n=20) Zero/SetZero/ByteArray/size=64-4 14.14n ± 0% 13.70n ± 0% -3.15% (p=0.000 n=20) Zero/SetZero/ByteArray/size=1024-4 24.22n ± 0% 20.18n ± 0% -16.68% (p=0.000 n=20) Zero/SetZero/BigStruct/size=1024-4 24.24n ± 0% 20.18n ± 0% -16.73% (p=0.000 n=20) Zero/SetZero/SmallStruct/size=16-4 13.45n ± 0% 13.10n ± 0% -2.60% (p=0.000 n=20) Zero/SetZero/SmallStructArray/size=64-4 14.12n ± 0% 13.69n ± 0% -3.05% (p=0.000 n=20) Zero/SetZero/SmallStructArray/size=1024-4 24.62n ± 0% 21.61n ± 0% -12.26% (p=0.000 n=20) Zero/SetZero/Time/size=24-4 13.59n ± 0% 13.40n ± 0% -1.40% (p=0.000 n=20) geomean 14.06n 10.19n -27.54% Finally, here are results from the benchmark example from #71323. Note however that almost all the benefit shown here is from our earlier CL 649555, which is a more general purpose change and eliminates the allocation using a different read-only global than this CL. │ go1.24 │ new │ │ sec/op │ sec/op vs base │ InterfaceAny 112.6000n ± 5% 0.8078n ± 3% -99.28% (p=0.000 n=20) ReflectValue 11.63n ± 2% 11.59n ± 0% ~ (p=0.330 n=20) │ go1.24.out │ new.out │ │ B/op │ B/op vs base │ InterfaceAny 224.0 ± 0% 0.0 ± 0% -100.00% (p=0.000 n=20) ReflectValue 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹ │ go1.24.out │ new.out │ │ allocs/op │ allocs/op vs base │ InterfaceAny 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=20) ReflectValue 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹ Updates #71359 Updates #71323 Change-Id: I64d8cf1a7900f011d2ec59b948388aeda1150676 Reviewed-on: https://go-review.googlesource.com/c/go/+/649078 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: David Chase <drchase@google.com>
2025-02-12 18:45:42 -05:00
sink = S{} // ERROR "using global for zero value interface value"
}
func struct4() {
v := S{}
cmd/compile/internal/walk: use global zeroVal in interface conversions for zero values This is a small-ish adjustment to the change earlier in our stack in CL 649555, which started creating read-only global storage for a composite literal used in an interface conversion and setting the interface data pointer to point to that global storage. In some cases, there are execution-time performance benefits to point to runtime.zeroVal in particular. In reflect, pointer checks against the runtime.zeroVal memory address are used to side-step some work, such as in reflect.Value.Set and reflect.Value.IsZero. In this CL, we therefore dig up the zeroVal symbol, and we use the machinery from earlier in our stack to use a pointer to zeroVal for the interface data pointer if we see examples like: sink = S{} or: s := S{} sink = s CL 649076 (also earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. We add a benchmark in reflect to show examples of performance benefit. The left column is our immediately prior CL 649555, and the right is this CL. (The arrays of structs here do not seem to benefit, which we attempt to address in our next CL). goos: linux goarch: amd64 pkg: reflect cpu: Intel(R) Xeon(R) CPU @ 2.80GHz │ cl-649555 │ new │ │ sec/op │ sec/op vs base │ Zero/IsZero/ByteArray/size=16-4 4.176n ± 0% 4.171n ± 0% ~ (p=0.151 n=20) Zero/IsZero/ByteArray/size=64-4 6.921n ± 0% 3.864n ± 0% -44.16% (p=0.000 n=20) Zero/IsZero/ByteArray/size=1024-4 21.210n ± 0% 3.878n ± 0% -81.72% (p=0.000 n=20) Zero/IsZero/BigStruct/size=1024-4 25.505n ± 0% 5.061n ± 0% -80.15% (p=0.000 n=20) Zero/IsZero/SmallStruct/size=16-4 4.188n ± 0% 4.191n ± 0% ~ (p=0.106 n=20) Zero/IsZero/SmallStructArray/size=64-4 8.639n ± 0% 8.636n ± 0% ~ (p=0.973 n=20) Zero/IsZero/SmallStructArray/size=1024-4 79.99n ± 0% 80.06n ± 0% ~ (p=0.213 n=20) Zero/IsZero/Time/size=24-4 7.232n ± 0% 3.865n ± 0% -46.56% (p=0.000 n=20) Zero/SetZero/ByteArray/size=16-4 13.47n ± 0% 13.09n ± 0% -2.78% (p=0.000 n=20) Zero/SetZero/ByteArray/size=64-4 14.14n ± 0% 13.70n ± 0% -3.15% (p=0.000 n=20) Zero/SetZero/ByteArray/size=1024-4 24.22n ± 0% 20.18n ± 0% -16.68% (p=0.000 n=20) Zero/SetZero/BigStruct/size=1024-4 24.24n ± 0% 20.18n ± 0% -16.73% (p=0.000 n=20) Zero/SetZero/SmallStruct/size=16-4 13.45n ± 0% 13.10n ± 0% -2.60% (p=0.000 n=20) Zero/SetZero/SmallStructArray/size=64-4 14.12n ± 0% 13.69n ± 0% -3.05% (p=0.000 n=20) Zero/SetZero/SmallStructArray/size=1024-4 24.62n ± 0% 21.61n ± 0% -12.26% (p=0.000 n=20) Zero/SetZero/Time/size=24-4 13.59n ± 0% 13.40n ± 0% -1.40% (p=0.000 n=20) geomean 14.06n 10.19n -27.54% Finally, here are results from the benchmark example from #71323. Note however that almost all the benefit shown here is from our earlier CL 649555, which is a more general purpose change and eliminates the allocation using a different read-only global than this CL. │ go1.24 │ new │ │ sec/op │ sec/op vs base │ InterfaceAny 112.6000n ± 5% 0.8078n ± 3% -99.28% (p=0.000 n=20) ReflectValue 11.63n ± 2% 11.59n ± 0% ~ (p=0.330 n=20) │ go1.24.out │ new.out │ │ B/op │ B/op vs base │ InterfaceAny 224.0 ± 0% 0.0 ± 0% -100.00% (p=0.000 n=20) ReflectValue 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹ │ go1.24.out │ new.out │ │ allocs/op │ allocs/op vs base │ InterfaceAny 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=20) ReflectValue 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹ Updates #71359 Updates #71323 Change-Id: I64d8cf1a7900f011d2ec59b948388aeda1150676 Reviewed-on: https://go-review.googlesource.com/c/go/+/649078 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: David Chase <drchase@google.com>
2025-02-12 18:45:42 -05:00
sink = v // ERROR "using global for zero value interface value"
}
func struct5() {
cmd/compile/internal/walk: convert composite literals to interfaces without allocating Today, this interface conversion causes the struct literal to be heap allocated: var sink any func example1() { sink = S{1, 1} } For basic literals like integers that are directly used in an interface conversion that would otherwise allocate, the compiler is able to use read-only global storage (see #18704). This CL extends that to struct and array literals as well by creating read-only global storage that is able to represent for example S{1, 1}, and then using a pointer to that storage in the interface when the interface conversion happens. A more challenging example is: func example2() { v := S{1, 1} sink = v } In this case, the struct literal is not directly part of the interface conversion, but is instead assigned to a local variable. To still avoid heap allocation in cases like this, in walk we construct a cache that maps from expressions used in interface conversions to earlier expressions that can be used to represent the same value (via ir.ReassignOracle.StaticValue). This is somewhat analogous to how we avoided heap allocation for basic literals in CL 649077 earlier in our stack, though here we also need to do a little more work to create the read-only global. CL 649076 (also earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. See the writeup in #71359 for details. Fixes #71359 Fixes #71323 Updates #62653 Updates #53465 Updates #8618 Change-Id: I8924f0c69ff738ea33439bd6af7b4066af493b90 Reviewed-on: https://go-review.googlesource.com/c/go/+/649555 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-13 23:49:50 -05:00
var a any = S{1, 1} // ERROR "using global for interface value"
_ = a
}
func struct6() {
var a any
v := S{1, 1}
cmd/compile/internal/walk: convert composite literals to interfaces without allocating Today, this interface conversion causes the struct literal to be heap allocated: var sink any func example1() { sink = S{1, 1} } For basic literals like integers that are directly used in an interface conversion that would otherwise allocate, the compiler is able to use read-only global storage (see #18704). This CL extends that to struct and array literals as well by creating read-only global storage that is able to represent for example S{1, 1}, and then using a pointer to that storage in the interface when the interface conversion happens. A more challenging example is: func example2() { v := S{1, 1} sink = v } In this case, the struct literal is not directly part of the interface conversion, but is instead assigned to a local variable. To still avoid heap allocation in cases like this, in walk we construct a cache that maps from expressions used in interface conversions to earlier expressions that can be used to represent the same value (via ir.ReassignOracle.StaticValue). This is somewhat analogous to how we avoided heap allocation for basic literals in CL 649077 earlier in our stack, though here we also need to do a little more work to create the read-only global. CL 649076 (also earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. See the writeup in #71359 for details. Fixes #71359 Fixes #71323 Updates #62653 Updates #53465 Updates #8618 Change-Id: I8924f0c69ff738ea33439bd6af7b4066af493b90 Reviewed-on: https://go-review.googlesource.com/c/go/+/649555 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-13 23:49:50 -05:00
a = v // ERROR "using global 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
}
cmd/compile/internal/escape: propagate constants to interface conversions to avoid allocs Currently, the integer value in the following interface conversion gets heap allocated: v := 1000 fmt.Println(v) In contrast, this conversion does not currently cause the integer value to be heap allocated: fmt.Println(1000) The second example is able to avoid heap allocation because of an optimization in walk (by Josh in #18704 and related issues) that recognizes a literal is being used. In the first example, that optimization is currently thwarted by the literal getting assigned to a local variable prior to use in the interface conversion. This CL propagates constants to interface conversions like in the first example to avoid heap allocations, instead using a read-only global. The net effect is roughly turning the first example into the second. One place this comes up in practice currently is with logging or debug prints. For example, if we have something like: func conditionalDebugf(format string, args ...interface{}) { if debugEnabled { fmt.Fprintf(io.Discard, format, args...) } } Prior to this CL, this integer is heap allocated, even when the debugEnabled flag is false, and even when the compiler inlines conditionalDebugf: v := 1000 conditionalDebugf("hello %d", v) With this CL, the integer here is no longer heap allocated, even when the debugEnabled flag is enabled, because the compiler can now see that it can use a read-only global. See the writeup in #71359 for more details. CL 649076 (earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. Updates #71359 Updates #62653 Updates #53465 Updates #8618 Change-Id: I19a51e74b36576ebb0b9cf599267cbd2bd847ce4 Reviewed-on: https://go-review.googlesource.com/c/go/+/649079 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@google.com>
2025-02-12 18:55:04 -05:00
// Some light emulation of conditional debug printing (such as in #53465).
func Printf(format string, args ...any) {
for _, arg := range args {
sink = arg
}
}
var enabled = true
func debugf(format string, args ...interface{}) {
if enabled {
Printf(format, args...)
}
}
//go:noinline
func debugf2(format string, args ...interface{}) {
if enabled {
Printf(format, args...)
}
}
func f1() {
v := 1000
debugf("hello %d", v) // ERROR "using global for interface value"
}
func f2() {
v := 1000
debugf2("hello %d", v) // ERROR "using global for interface value"
}
//go:noinline
func f3(i int) {
debugf("hello %d", i)
}
func f4() {
f3(1000)
}