go ``` cmd/compile: 优化异或操作以得到补码 ```

cgh8pdjw  于 6个月前  发布在  Go
关注(0)|答案(4)|浏览(53)

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

$ go version
go version go1.14.4 linux/amd64

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

是的。

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

go env 输出

$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/jadenw/.cache/go-build"
GOENV="/home/jadenw/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/jadenw/GOPATH"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/lib/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
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-build102234605=/tmp/go-build -gno-record-gcc-switches"

你做了什么?

尝试使用 ^= ^uint64(0) 在循环中翻转位图中的一大片区域的位。

你期望看到什么?

编译器将此视为按位取反操作。

你看到了什么?

编译器对一个常量进行异或操作,并将其处理得与按位取反不同。
https://godbolt.org/z/vxQyD3
如果我理解正确的话,可以通过向 generic.rules 添加以下规则来解决这个问题:

(Xor(64|32|16|8) (Const(64|32|16|8) [-1]) x) => (Com(64|32|16|8) x)
eblbsuwk

eblbsuwk1#

Sure, that would work.
For some architectures it won't matter, as XOR -1, R and NOT R are, as far as the chip is concerned, the same instruction. It helps a bit on x86 for code size.

twh00eeo

twh00eeo2#

建议评估具有超过两个操作数的表达式,例如:
res := -1 ^ x ^ const_c
根据我们最近对arm64进行重写'MVN XOR'到EON的研究(https://go-review.googlesource.com/c/go/+/239638),过早地简化'-1 ^ x'可能导致上述场景(NOT + XOR vs. XOR)的非优化代码,因为当前generic.rules中的规则更倾向于首先关联常量。

7gcisfzg

7gcisfzg3#

如果我们想要为了优化目的使它们互换,我们可能需要一些规则,如下:

(Xor(64|32|16|8) (Const(64|32|16|8) [c]) (Com(64|32|16|8) x)) => (Xor(64|32|16|8) (Const(64|32|16|8) [^c]) x)
(Com(64|32|16|8) (Xor(64|32|16|8) (Const(64|32|16|8) [c]) x)) => (Xor(64|32|16|8) (Const(64|32|16|8) [^c]) x)

在这一点上,我不禁怀疑在这个编译阶段存在 Com 是否真的有意义。也许这应该始终表示为一个 Xor 操作,并通过平台上的补码规则转换为补码?

wvmv3b1j

wvmv3b1j4#

我们尝试将常量提升到最外层,然后折叠它们。例如,我们有以下关于异或的规则:

// x ^ (C ^ z) -> C ^ (x ^ z)
(Xor64 (Xor64 i:(Const64 <t>) z) x) && (z.Op != OpConst64 && x.Op != OpConst64) => (Xor64 i (Xor64 <t> z x))
// C ^ (D ^ x) -> (C ^ D) ^ x
(Xor64 (Const64 <t> [c]) (Xor64 (Const64 <t> [d]) x)) => (Xor64 (Const64 <t> [c^d]) x)

这应该对这个例子至少在generic.rules中做正确的事情。当我们降低时,确实需要规则来组合(对于amd64)NOT和XORconst。它们应该不难实现。
是的,我们可以在通用级别消除Com。我不确定这能给我们带来多少好处,因为NOTs可以在其他方面以arch-specific级别引入,因此arch-specific规则需要知道如何组合XOR和NOT。
但我不反对如果它简化了事情就移除Com。

相关问题