你正在使用的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)
4条答案
按热度按时间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.
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中的规则更倾向于首先关联常量。
7gcisfzg3#
如果我们想要为了优化目的使它们互换,我们可能需要一些规则,如下:
在这一点上,我不禁怀疑在这个编译阶段存在
Com
是否真的有意义。也许这应该始终表示为一个Xor
操作,并通过平台上的补码规则转换为补码?wvmv3b1j4#
我们尝试将常量提升到最外层,然后折叠它们。例如,我们有以下关于异或的规则:
这应该对这个例子至少在generic.rules中做正确的事情。当我们降低时,确实需要规则来组合(对于amd64)NOT和XORconst。它们应该不难实现。
是的,我们可以在通用级别消除Com。我不确定这能给我们带来多少好处,因为NOTs可以在其他方面以arch-specific级别引入,因此arch-specific规则需要知道如何组合XOR和NOT。
但我不反对如果它简化了事情就移除Com。