Go version
go version go1.26.3 windows/amd64
Output of go env in your module/workspace:
set AR=ar
set CC=gcc
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_ENABLED=1
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set CXX=g++
set GCCGO=gccgo
set GO111MODULE=
set GOAMD64=v4
set GOARCH=amd64
set GOAUTH=netrc
set GOBIN=E:\Programs\go
set GOCACHE=C:\Users\Omion\AppData\Local\go-build
set GOCACHEPROG=
set GODEBUG=
set GOENV=C:\Users\Omion\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=simd
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\Omion\AppData\Local\Temp\go-build1682809908=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=D:\Documents\git\test\ascon\go.mod
set GOMODCACHE=C:\Users\Omion\go\pkg\mod
set GONOPROXY=rutile.org
set GONOSUMDB=rutile.org
set GOOS=windows
set GOPATH=C:\Users\Omion\go
set GOPRIVATE=rutile.org
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=E:\Programs\Go
set GOSUMDB=sum.golang.org
set GOTELEMETRY=local
set GOTELEMETRYDIR=C:\Users\Omion\AppData\Roaming\go\telemetry
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=E:\Programs\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.26.3
set GOWORK=
set PKG_CONFIG=pkg-config
What did you do?
While researching my previous issue #79666 , I noticed that using (archsimd.vectorType).Not() would prevent the rewriteTern() function from rewriting it. The code seemed to exist to handle it, but it was almost never triggered.
The code I'm using is from test/simd: x.Xor(y).Xor(z).And(x.Xor(y).Xor(z.Not())) should turn into VPTERNLOGQ $0, Z0, Z1, Z2 (or some other combination of vector registers). This could be further optimized into a simple VPXORQ Z2, Z2, but that is a different issue.
func Not512(x, y, z Int64x8) Int64x8 {
return x.Xor(y).Xor(z).And(x.Xor(y).Xor(z.Not()))
}
func Not512Conditional(x, y, z Int64x8) Int64x8 {
if X86.AVX512() {
return x.Xor(y).Xor(z).And(x.Xor(y).Xor(z.Not()))
} else {
return x.Xor(y).Xor(z).And(x.Xor(y).Xor(z.Not()))
}
}
What did you see happen?
Generated assembly is:
TEXT main.Not512(SB), NOSPLIT|NOFRAME|ABIInternal, $0-192
VPXORQ Z0, Z1, Z3
VMOVDQU64 Z2, Z4
VPTERNLOGQ $150, Z0, Z1, Z2
VPCMPEQQ Z4, Z4, K1
VPMOVM2Q K1, Z1
VPXORQ Z4, Z1, Z1
VPTERNLOGQ $40, Z2, Z1, Z3
NOP
VMOVDQU64 Z3, Z0
RET
This function has essentially turned into two VPTERNLOGQ instructions separated by the code that is generated by a .Not(): VPCMPEQQ / VPMOVM2Q / VPXORQ, when all of it should have been replaced by a single VPTERNLOGQ.
TEXT main.Not512Conditional(SB), NOSPLIT|NOFRAME|ABIInternal, $0-192
NOP
XCHGL AX, AX
CMPB internal/cpu.X86+69(SB), $0
JEQ 24
VPTERNLOGQ $0, Z0, Z1, Z2
VMOVDQU64 Z2, Z0
RET
VPTERNLOGQ $0, Z0, Z1, Z2
VMOVDQU64 Z2, Z0
RET
When using a conditional with the same code on both branches, the compiler correctly rewrites both branches to use a single VPTERNLOGQ instruction, even though this rewrite does not happen without the conditional.
Since 512-bit .Not() is not recognized correctly, and smaller vectors can't expect AVX-512 support without additional checks, this means that .Not() is never combined into a VPTERNLOG instruction in any situation outside an explicit check for AVX-512.
What did you expect to see?
All 512-bit code paths could have returned a single VPTERNLOGQ instruction to replace the logic.
As with issue #79666 , this merely appears to be a bug in an optimization step; the generated code is correct in all cases that I have tried.
I had a theory that the optimization failure had to do with the 512-bit Not() method turning into:
VPCMPEQQ Z0, Z0, K1
VPMOVM2Q K1, Z1
VPXORQ Z1, Z0, Z0
whereas without AVX-512 it turns into:
VPCMPEQQ Y0, Y0, Y1
VPXOR Y1, Y0, Y0
Perhaps the extra mask conversion messes up the pattern recognition? This wouldn't explain why it's correctly optimized when there is a check on X86.AVX512(), though.
Go version
go version go1.26.3 windows/amd64
Output of
go envin your module/workspace:What did you do?
While researching my previous issue #79666 , I noticed that using (archsimd.vectorType).Not() would prevent the rewriteTern() function from rewriting it. The code seemed to exist to handle it, but it was almost never triggered.
The code I'm using is from test/simd:
x.Xor(y).Xor(z).And(x.Xor(y).Xor(z.Not()))should turn intoVPTERNLOGQ $0, Z0, Z1, Z2(or some other combination of vector registers). This could be further optimized into a simpleVPXORQ Z2, Z2, but that is a different issue.What did you see happen?
Generated assembly is:
This function has essentially turned into two VPTERNLOGQ instructions separated by the code that is generated by a .Not():
VPCMPEQQ / VPMOVM2Q / VPXORQ, when all of it should have been replaced by a single VPTERNLOGQ.When using a conditional with the same code on both branches, the compiler correctly rewrites both branches to use a single VPTERNLOGQ instruction, even though this rewrite does not happen without the conditional.
Since 512-bit .Not() is not recognized correctly, and smaller vectors can't expect AVX-512 support without additional checks, this means that .Not() is never combined into a VPTERNLOG instruction in any situation outside an explicit check for AVX-512.
What did you expect to see?
All 512-bit code paths could have returned a single VPTERNLOGQ instruction to replace the logic.
As with issue #79666 , this merely appears to be a bug in an optimization step; the generated code is correct in all cases that I have tried.
I had a theory that the optimization failure had to do with the 512-bit Not() method turning into:
whereas without AVX-512 it turns into:
Perhaps the extra mask conversion messes up the pattern recognition? This wouldn't explain why it's correctly optimized when there is a check on X86.AVX512(), though.