go `cmd/compile:检测并应用更多的CMOV优化`

moiiocjp  于 4个月前  发布在  Go
关注(0)|答案(1)|浏览(78)

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

go版本:go1.11 linux/amd64

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

是的。并且已经和master版本进行了对比(go版本:devel+7f3de1f275 Thu Sep 20 14:44:04 2018 +0530 linux/amd64)

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

GOARCH="amd64"
GOBIN=""
GOCACHE="/home/agniva/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/agniva/play/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build407760751=/tmp/go-build -gno-record-gcc-switches"

你做了什么?

考虑以下代码 -

package main

func min2(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func min3(a, b, c int) int {
	if a < b {
		if a < c {
			return a
		}
	} else if b < c {
		return b
	}
	return c
}

var a, b, c int
var d int

func main() {
	d = min2(min2(a, b), c)
	d = min3(a, b, c)
}

你期望看到什么?

这里,两个函数都试图获取3个整数的最小值,尽管方式略有不同。第一个函数执行两次2个整数的最小值,第二个函数在一个函数中执行它们。人们可能会认为编译器足够智能,生成的代码应该等效。或者至少,不应该比另一个更差。

你看到了什么?

但是在调查汇编代码时,我们看到了有趣的结果

1st one
 0x0000 00000 (funcreg.go:34)    MOVQ    "".c(SB), AX
        0x0007 00007 (funcreg.go:34)    MOVQ    "".a(SB), CX
        0x000e 00014 (funcreg.go:34)    MOVQ    "".b(SB), DX
        0x0015 00021 (funcreg.go:34)    CMPQ    CX, DX
        0x0018 00024 (funcreg.go:34)    CMOVQLT CX, DX
        0x001c 00028 (funcreg.go:34)    CMPQ    DX, AX
        0x001f 00031 (funcreg.go:34)    CMOVQLT DX, AX
        0x0023 00035 (funcreg.go:34)    MOVQ    AX, "".d(SB)

2nd one
        0x002a 00042 (funcreg.go:35)    MOVQ    "".c(SB), AX
        0x0031 00049 (funcreg.go:35)    MOVQ    "".a(SB), CX
        0x0038 00056 (funcreg.go:35)    MOVQ    "".b(SB), DX
        0x003f 00063 (funcreg.go:35)    CMPQ    CX, DX
        0x0042 00066 (funcreg.go:34)    JGE     86
        0x0044 00068 (funcreg.go:35)    CMPQ    CX, AX
        0x0047 00071 (funcreg.go:35)    JGE     81
        0x0049 00073 (funcreg.go:35)    MOVQ    CX, "".d(SB)
        0x0050 00080 (funcreg.go:36)    RET
        0x0051 00081 (funcreg.go:35)    MOVQ    AX, CX
        0x0054 00084 (funcreg.go:35)    JMP     73
        0x0056 00086 (funcreg.go:35)    CMPQ    DX, AX
        0x0059 00089 (funcreg.go:35)    JGE     81
        0x005b 00091 (funcreg.go:35)    MOVQ    DX, CX
        0x005e 00094 (funcreg.go:35)    JMP     73

注意到在第二种情况下没有生成CMOV指令。
只有当我们将min3代码更改为像min2那样的行为时 -

func min3(a, b, c int) int {
	min := b
	if a < b {
		min = a
	}
	if c < min {
		min = c
	}
	return min
}

然后我们得到了CMOV指令。但这只是在我意识到编译器是如何将代码转换为显式生成CMOV指令之后才发生的。
最初在golang-dev线程中讨论过 - https://groups.google.com/forum/#!topic/golang-dev/JaYi4D-tsbY.
/cc @randall77@rasky

s4n0splo

s4n0splo1#

In-lining a comment from @philhofer from the golang-dev thread:
Author of the original branchelim code here.
The reason the example with returns doesn't get optimized is because the compiler doesn't do return block unification.
Keith wrote a patch once to implement it, but it was never merged.
The optimization has no trouble rewriting that function once it is inlined because the target of the 'return' branch
is simply another basic block in the function. (In some sense, inlining has to implement return block unification.)
(At least, I think that was from @philhofer).

相关问题