Filed on behalf of Jan Mercl (from a golang-dev thread).
go version)?$ go version
go version go1.15.2 linux/amd64
Yes.
go env)?$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/jnml/.cache/go-build"
GOENV="/home/jnml/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/jnml/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/jnml"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/jnml/src/go.googlesource.com/go/goroot"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/jnml/src/go.googlesource.com/go/goroot/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-build571607678=/tmp/go-build -gno-record-gcc-switches"
jnml@e5-1650:~/tmp/csmith$ cat main.go
package foo
import "modernc.org/libc"
func safe_div_func_int16_t_s_s(tls *libc.TLS, si1 int16, si2 int16) int16 {
if (int32(si2) == 0) || ((int32(si1) == (-32767 - 1)) && (int32(si2) == (-1))) {
return si1
}
return (int16(int32(si1) / int32(si2)))
}
func safe_lshift_func_int16_t_s_u(tls *libc.TLS, left int16, right uint32) int16 {
if ((int32(left) < 0) || ((right) >= uint32(32))) || (int32(left) > (int32((32767)) >> (right))) {
return left
}
return (int16(int32(left) << (right)))
}
func safe_sub_func_int32_t_s_s(tls *libc.TLS, si1 int32, si2 int32) int32 {
if ((si1 ^ si2) & (((si1 ^ ((si1 ^ si2) & (^int32(libc.Int32FromInt32(2147483647))))) - si2) ^ si2)) < 0 {
return si1
}
return (si1 - si2)
}
func safe_unary_minus_func_int64_t_s(tls *libc.TLS, si int64) int64 {
if si == (-9223372036854775807 - int64(1)) {
return si
}
return -si
}
func safe_sub_func_uint16_t_u_u(tls *libc.TLS, ui1 uint16, ui2 uint16) uint16 {
return (uint16(int32(ui1) - int32(ui2)))
}
func func_44(tls *libc.TLS) uint16 {
return safe_sub_func_uint16_t_u_u(tls, (uint16((int32(safe_div_func_int16_t_s_s(tls, 0, int16(safe_sub_func_int32_t_s_s(tls, 1, int32(safe_lshift_func_int16_t_s_u(tls, int16(0x1504), uint32(safe_unary_minus_func_int64_t_s(tls, int64(libc.Bool32(true))))))))))))), 0)
}
jnml@e5-1650:~/tmp/csmith$ go build main.go
Nothing but a command prompt.
# command-line-arguments
./main.go:38:2: invalid instruction: 00105 (/home/jnml/tmp/csmith/main.go:38) MOVL $11553462026240, AX
jnml@e5-1650:~/tmp/csmith$
cc @randall77
I'm trying to reproduce, this has the look and feel of a bug I thought I could create on MIPS, that I (also) encountered with late call expansion. Basically, if you can smuggle a funny constant past the early phases and have it appear "late" (e.g., within late call expansion, which is not in this compiler yet, or in prove, which is), surprising things can happen. I solved it for MIPS by adding some code generation rules, but I don't see the same missing rules on AMD64 so it is not an exact duplicate.
No, it's weirder than that. Compare compilation of the two MOVLconst <int32> [0] : AX in the code:
b28: ← b16-
v24 (9) = MOVLconst <int32> [0] : AX
v102 (+9) = DIVL <int32,int32> [false] v24 v85 : <AX,DX>
v106 (+9) = Select0 <int32> v102 : AX
Plain → b30 (+38)
b10: ← b8-
v43 (13) = MOVLconst <int32> [32767] : CX
v42 (13) = SARLconst <int32> [31] v43 : CX
v20 (13) = CMPLconst <flags> [5380] v42
LT v20 → b13 b12 (13)
b12: ← b10-
v105 (16) = MOVLconst <int32> [0] : AX
Plain → b16 (+38)
v42 00030 (+9) XORL AX, AX
v102 00031 (9) CMPL CX, $-1
v102 00032 (9) JEQ 36
v102 00033 (9) CDQ
v102 00034 (9) IDIVL CX
v102 00035 (9) JMP 38
v102 00036 (9) NEGL AX
v102 00037 (9) XORL DX, DX
b28 00038 (+38) JMP 28
v43 00039 (13) MOVL $32767, CX
v42 00040 (13) SARL $31, CX
v20 00041 (13) CMPL CX, $5380
b10 00042 (13) JLT 8
v105 00043 (16) MOVL $11553462026240, AX
b12 00044 (+38) JMP 9
I've got other things to work on, just wanted to be sure that wasn't a variant of the bug I fixed just the other day for MIPS.
FWIW, a slightly more contained repro - no imports and simplied where possible:
https://play.golang.org/p/rB2HvJwS5j3
v105 (16) = MOVLconst <int32> [0] : AX
v105 00043 (16) MOVL $11553462026240, AX
This looks to me like a rule isn't sign extending an int32 constant to 64 bits. The pretty printer will just ignore the high bits but the MOVL will get a copy of the raw int64 value. I bet the new typed rules in master will fix it.
I got curious and took a quick look at the AMD64 rules in Go 1.15. This looks like the sort of rule that could cause an issue like this:
(The sign extension to 64-bits happens before the shift and not after, so the resulting value could be out-of-range for an int32. This particular rule probably doesn't trigger very often because the generic rules do most constant folding.)
A MOVLconst that doesn't have its high bits sign-extended correctly should fail if the check pass is enabled. Does it?
Yep (based on https://github.com/golang/go/issues/41711#issuecomment-701610857).
$ ./go1.15/bin/go tool compile -d=ssa/check/on example.go
example.go:37:35: internal compiler error: 'func_44': bad int32 AuxInt value for v43
Please file a bug report including a short program that triggers the error.
https://golang.org/issue/new
@gopherbot please open a backport issue for 1.15.
Seems to be ok for 1.14 and earlier.
Backport issue(s) opened: #41720 (for 1.15).
Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://golang.org/wiki/MinorReleases.
Also ok at tip. Just 1.15 needs fixing.
Change https://golang.org/cl/258817 mentions this issue: [release-branch.go1.15] cmd/compile: fix left shift constant folding rule
@randall77 , is it okay to close this issue now that the 1.15 CL is in?
Yes.