go `cmd/compile:` 在存在多个示例化时,不内联泛型类型的通用方法,

hivapdat  于 4个月前  发布在  Go
关注(0)|答案(4)|浏览(38)

你正在使用的Go版本是什么( go version )?

$ go version
go version go1.20.2 darwin/arm64

这个问题在最新版本的发布中是否会重现?

你正在使用什么操作系统和处理器架构( go env )?

go env 输出

$ go env
GO111MODULE=""
GOARCH="arm64"
GOBIN=""
GOCACHE="/Users/r/Library/Caches/go-build"
GOENV="/Users/r/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/r/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/r/go"
GOPRIVATE=""
GOPROXY="https://goproxy.cn,direct"
GOROOT="/Users/r/sdk/go1.20.2"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/Users/r/sdk/go1.20.2/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.20.2"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/r/workspace/testcode/testinlinegeneric/go.mod"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/j_/rq9ph2cd3h50w468sgv34lwh0000gn/T/go-build2389814434=/tmp/go-build -gno-record-gcc-switches -fno-common"
GOROOT/bin/go version: go version go1.20.2 darwin/arm64
GOROOT/bin/go tool compile -V: compile version go1.20.2
uname -v: Darwin Kernel Version 22.3.0: Mon Jan 30 20:38:37 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T6000
ProductName:		macOS
ProductVersion:		13.2.1
BuildVersion:		22D68
lldb --version: lldb-1400.0.38.17
Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)

你做了什么?

go.mod:

module test/inlinegeneric

go 1.20

a/a.go:

package a

type Foo1[T any] struct {
}

func (*Foo1[T]) Run() {
}

type Foo2 struct {
}

func (*Foo2) Run() {
}

b/b.go:

package b

import "test/inlinegeneric/a"

func Bar() {
	var f1 a.Foo1[int]
	f1.Run()
	var f2 a.Foo2
	f2.Run()
}

type Boo struct {
	f1 a.Foo1[int]
	f2 a.Foo2
}

main.go:

package main

import "test/inlinegeneric/b"

func main() {
	var x b.Boo
	_ = x
	b.Bar()
}

go1.20.2 build --gcflags="-m=2" ./main.go
go1.19.7 build --gcflags="-m=2" ./main.go

你期望看到什么?

对于go1.20.2:

# command-line-arguments
./main.go:5:6: can inline main with cost 20 as: func() { b.Bar() }
./main.go:8:7: inlining call to b.Bar
./main.go:8:7: inlining call to a.(*Foo1[go.shape.int]).Run
./main.go:8:7: inlining call to a.(*Foo2).Run

对于go1.19.7

# command-line-arguments
./main.go:5:6: can inline main with cost 20 as: func() { b.Bar() }
./main.go:8:7: inlining call to b.Bar
./main.go:8:7: inlining call to a.(*Foo1[go.shape.int_0]).Run
./main.go:8:7: inlining call to a.(*Foo2).Run

你实际上看到了什么?

对于go1.20.2:

# command-line-arguments
./main.go:5:6: can inline main with cost 27 as: func() { x = <nil>; _ = x; b.Bar() }
./main.go:8:7: inlining call to b.Bar
./main.go:8:7: inlining call to a.(*Foo2).Run
./a/a.go:6:6: can inline a.(*Foo1[go.shape.int]).Run with cost 0 as: method(*a.Foo1[go.shape.int]) func(*[0]uintptr) {  }
./a/a.go:6:6: can inline a.(*Foo1[int]).Run with cost 9 as: method(*a.Foo1[int]) func() { var .autotmp_0 *a.Foo1[int]; .autotmp_0 = <nil>; (*a.Foo1[go.shape.int]).Run(.autotmp_0, &a..dict.Foo1[int]) }
./a/a.go:6:6: inlining call to a.(*Foo1[go.shape.int]).Run

对于go1.19.7

# command-line-arguments
./main.go:5:6: can inline main with cost 27 as: func() { var x b.Boo; x = <nil>; _ = x; b.Bar() }
./main.go:8:7: inlining call to b.Bar
./main.go:8:7: inlining call to a.(*Foo2).Run
./a/a.go:6:17: can inline a.(*Foo1[go.shape.int_0]).Run with cost 0 as: func(uintptr, *a.Foo1[go.shape.int_0]) {  }
ehxuflar

ehxuflar1#

See #57505 . Closing as a dup.

dvtswwa3

dvtswwa32#

See #57505 . Closing as a dup.
I don't think this issue is same as #57505 .
Neither go1.19 nor go1.20 nor go1.20 with non-unified does inline the method of the generic type.
But in #57505 , only go1.20 does not inline.
And I found that if we remove the field f1 in b.Boo struct or remove the variable x in main, then go1.20 will do inline the method, but go1.19 or non-unified will not inline.
b/b.go:

package b

func Bar() {
	var f1 Foo1[int]
	f1.Run()
	var f2 Foo2[int]
	f2.Run()
}

type Boo struct {
	f1 Foo1[int]
}

type Foo1[T any] struct {
}

func (*Foo1[T]) Run() {
}

type Foo2[T any] struct {
}

func (*Foo2[T]) Run() {
}

main.go:

package main

import "test/inlinegeneric/b"

func main() {
	var x b.Boo
	_ = x
	b.Bar()
}

go1.19.7 & go1.20 non-unified: None of the methods Run of Foo1[T] or Foo2[T] is inlined.

$ go1.19.7 build --gcflags="-m=2" ./main.go
# command-line-arguments
./main.go:5:6: can inline main with cost 29 as: func() { var x b.Boo; x = <nil>; _ = x; b.Bar() }
./main.go:8:7: inlining call to b.Bar
./b/b.go:17:17: can inline b.(*Foo1[go.shape.int_0]).Run with cost 0 as: func(uintptr, *b.Foo1[go.shape.int_0]) {  }

$ GOEXPERIMENT=nounified go1.20.2 build --gcflags="-m=2" ./main.go                                         [0](0s)[14:35:03]
# command-line-arguments
./main.go:5:6: can inline main with cost 29 as: func() { var x b.Boo; x = <nil>; _ = x; b.Bar() }
./main.go:8:7: inlining call to b.Bar
./b/b.go:17:17: can inline b.(*Foo1[go.shape.int_0]).Run with cost 0 as: func(uintptr, *b.Foo1[go.shape.int_0]) {  }

go1.20: Only Foo1[T].Run is not inlined, Foo2[T].Run is inlined. And the difference between Foo1[T] and Foo2[T] is that Boo has a field of type Foo1[T] and Boo is used in main package.

$ go1.20.2 build --gcflags="-m=2" ./main.go                                                             [146](38s)[14:35:00]
# command-line-arguments
./main.go:5:6: can inline main with cost 29 as: func() { x = <nil>; _ = x; b.Bar() }
./main.go:8:7: inlining call to b.Bar
./main.go:8:7: inlining call to b.(*Foo2[go.shape.int]).Run
./b/b.go:17:6: can inline b.(*Foo1[go.shape.int]).Run with cost 0 as: method(*b.Foo1[go.shape.int]) func(*[0]uintptr) {  }
./b/b.go:17:6: can inline b.(*Foo1[int]).Run with cost 9 as: method(*b.Foo1[int]) func() { var .autotmp_0 *b.Foo1[int]; .autotmp_0 = <nil>; (*b.Foo1[go.shape.int]).Run(.autotmp_0, &b..dict.Foo1[int]) }
./b/b.go:17:6: inlining call to b.(*Foo1[go.shape.int]).Run
sulc1iza

sulc1iza3#

你能明确指出哪些函数你希望内联,但没有吗?是 a.(*Foo1[go.shape.int]).Run 没有被内联到 main 中吗?仅仅粘贴诊断信息而不解释是不清晰的。谢谢。
我发现如果我们从 b.Boo 结构体中移除字段 f1 或者从 main 中移除变量 x,那么 go1.20 将内联方法。
所以,听起来 a.Foo1[int] 的两个示例,一个在 b.Boo 中,另一个在 b.Bar 中(内联到 main )导致编译器困惑?
此外,它似乎不需要两级导入。即使我将 Foo1Foo2 的定义放入包 b 中,似乎 (*Foo1[go.shape.int]).Run 仍然没有被内联到 main 中。
cc @mdempsky@thanm @golang/compiler

ar7v8xwq

ar7v8xwq4#

你能明确指出哪些函数你希望内联,但实际上没有内联吗?是 a.(*Foo1[go.shape.int]).Run 没有被内联到 main 吗?仅仅粘贴诊断信息而不解释不清楚。谢谢。

是的,a.(*Foo1[go.shape.int]).Run 预期会被内联到 main ,但实际上并没有。
我发现如果我们从 b.Boo 结构体中移除字段 f1 或者从 main 中移除变量 x,那么 go1.20 将会内联该方法。
所以,听起来 a.Foo1[int] 的两个示例(一个在 b.Boo 中,另一个在 b.Bar 中,内联到 main)导致了编译器的困惑?
是的。
此外,它似乎不需要两级导入。即使我将 Foo1Foo2 的定义放入 package b 中,似乎 (*Foo1[go.shape.int]).Run 仍然没有被内联到 main 中。
cc @mdempsky@thanm @golang/compiler
是的,不需要 package a

相关问题