你正在使用的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@b0056eb
dlv 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)。编译器注意到变量的值是常量,并优化掉了读取操作。
4条答案
按热度按时间mzmfm0qo1#
这看起来像是我们在
opt
传递中没有将必需的重写与可选的重写分开的结果。当我明确标记opt
和late opt
编译器传递为非必需时,问题就解决了。只有少数几个必需的重写规则;它们应该不难拆分出来。
代码中甚至有一条注解:
92vpleto2#
@randall77,我有一个问题想问你。为什么交换第22行和第23行后,这种情况不会发生?也许更大的问题是,为什么这种优化不适用于原始代码中的第23行?我希望函数调用的行为能够一致地应用,但似乎并非如此?这是否与函数期望的参数类型有关?
vmpqdwk33#
为什么在交换第22行和第23行时,这种情况不会发生?
这是两者之间的区别:
和
在前者中,一个简单的重写规则可以跟随"5"的流程到
f
的参数槽。在后者中,它需要知道g
不会修改v
。后来的知识通过-N
关闭了。也许更大的问题是,为什么这个优化不适用于原始代码中的第23行?我希望函数调用的行为能够一致地应用,但似乎并非如此?这是否与函数期望的参数类型有关?
这是因为
Sprintf
具有一个...
参数槽。有了-N
,...
切片的后备数组总是在堆上分配,而涉及到的newobject
调用充当上面的g
。fnvucqvd4#
翻译结果为:在Dive中,变量赋值仍然存在问题。
上面的线程提到:“当我明确标记opt和late opt编译器传递为不需要时,问题就解决了。”
允许用户进行这种标记编译器传递不需要的语法是什么?
附上的文件重现了这个问题。
rrm.go.txt