go cmd/compile: 当使用-gcflags="all=-N -l"时,变量读取不应被优化,

2jcobegt  于 4个月前  发布在  Go
关注(0)|答案(4)|浏览(45)

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

$ go version
go version go1.11.4 windows/amd64
go version go1.12beta2 windows/amd64

这个问题在最新的版本中是否重现?
是的
你正在使用的操作系统和处理器架构是什么( go env )?
go env 输出

$ go env
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\florin\AppData\Local\go-build
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=D:\go
set GOPROXY=
set GORACE=
set GOROOT=C:\Go1.12
set GOTMPDIR=
set GOTOOLDIR=C:\Go1.12\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=D:\awesomeProject9\go.mod
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 -fdebug-prefix-map=C:\Users\florin\AppData\Local\Temp\go-build732947326=/tmp/go-build -gno-record-gcc-switches

你做了什么?

  • 使用Go Modules支持和来自 https://play.golang.org/p/nnDfvOVbWk9 的代码创建一个项目
  • 运行 go-delve/delve@b0056ebdlv debug blah.go
  • 在第22行放置一个断点
  • 运行应用程序并使用 curl -i http://localhost:8080/ 发出请求
  • 在delve中运行 set returnStatus = 404 然后恢复应用程序

你期望看到什么?
调试器应该能够在调用发生之前更改函数参数的值。
你看到了什么?
调试器无法更改值,使用了旧值。
我的预期是,如果我使用 -gcflags="all=-N -l" 编译应用程序,这种优化将被移除,我可以使用我的程序进行调试。
我还注意到,如果我交换第22行和第23行,并保持对第22行的断点,结果将是预期的结果。所以这里的优化也不是一致应用的(我是否应该为它打开一个单独的bug?)。
根据原始问题中的@aarzilli,go-delve/delve#1473(评论):
那行编译成:

blah.go:22		0x750931		488b8424e0000000		MOVQ 0xe0(SP), AX		
  blah.go:22		0x750939		8400				TESTB AL, 0(AX)			
  blah.go:22		0x75093b		488b4028			MOVQ 0x28(AX), AX		
  blah.go:22		0x75093f		488b8c24e8000000		MOVQ 0xe8(SP), CX		
  blah.go:22		0x750947		48890c24			MOVQ CX, 0(SP)			
  blah.go:22		0x75094b		48c7442408c8000000		MOVQ $0xc8, 0x8(SP)		
  blah.go:22		0x750954		ffd0				CALL AX

感兴趣的指令是 0x75094b,它将一个函数参数直接设置为常量0xc8(200)。编译器注意到变量的值是常量,并优化掉了读取操作。

mzmfm0qo

mzmfm0qo1#

这看起来像是我们在opt传递中没有将必需的重写与可选的重写分开的结果。当我明确标记optlate opt编译器传递为非必需时,问题就解决了。
只有少数几个必需的重写规则;它们应该不难拆分出来。
代码中甚至有一条注解:

{name: "opt", fn: opt, required: true},              // TODO: split required rules and optimizing rules
92vpleto

92vpleto2#

@randall77,我有一个问题想问你。为什么交换第22行和第23行后,这种情况不会发生?也许更大的问题是,为什么这种优化不适用于原始代码中的第23行?我希望函数调用的行为能够一致地应用,但似乎并非如此?这是否与函数期望的参数类型有关?

vmpqdwk3

vmpqdwk33#

为什么在交换第22行和第23行时,这种情况不会发生?
这是两者之间的区别:

v := 5
f(v)
g()

v := 5
g()
f(v)

在前者中,一个简单的重写规则可以跟随"5"的流程到 f 的参数槽。在后者中,它需要知道 g 不会修改 v 。后来的知识通过 -N 关闭了。
也许更大的问题是,为什么这个优化不适用于原始代码中的第23行?我希望函数调用的行为能够一致地应用,但似乎并非如此?这是否与函数期望的参数类型有关?
这是因为 Sprintf 具有一个 ... 参数槽。有了 -N, ... 切片的后备数组总是在堆上分配,而涉及到的 newobject 调用充当上面的 g

fnvucqvd

fnvucqvd4#

翻译结果为:在Dive中,变量赋值仍然存在问题。

上面的线程提到:“当我明确标记opt和late opt编译器传递为不需要时,问题就解决了。”

允许用户进行这种标记编译器传递不需要的语法是什么?

附上的文件重现了这个问题。

rrm.go.txt

相关问题