Closed ianlancetaylor closed 5 years ago
According to a git bisect, the error was introduced at https://golang.org/cl/129875 (8dbd9afbb05c77ca8426256d172f9e05fe48a0f0).
CC @martisch
CC @dr2chase
CC @rasky
The comment at https://go-review.googlesource.com/c/go/+/129875/8/src/cmd/compile/internal/ssa/gen/PPC64.rules#903 says
// Note that MOV??reg returns a 64-bit int, x is not necessarily that wide
// This may interact with other patterns in the future. (Compare with arm64)
I think this is exactly the reason of this failure (yah, the "future" has come).
Before lower,
v10 (12) = Trunc64to32 <int32> v9 (i32[int32])
...
v29 (+15) = ZeroExt32to64 <uint64> v10
v30 (15) = Neq64 <bool> v27 v29
If v30 → b6 b4 (15)
Note that v29 has a 64-bit type.
After lower,
v10 (12) = MOVWZreg <int32> v9 (i32[int32])
...
v28 (+15) = CMP <flags> v27 v10
NE v28 → b6 b4 (15)
Note that v10, MOVWZ is a 32-bit to 64-bit zero extension, but has type int32. And the rule folds v10 to v29, which folds into the CMP, v28.
After regalloc,
v10 (12) = MOVWZreg <int32> v9 : R5 (i32[int32])
v42 (12) = StoreReg <int32> v10 : i32[int32]
...
v30 (15) = LoadReg <int32> v42 : R4
v28 (+15) = CMP <flags> v27 v30
NE v28 → b6 b4 (unlikely) (15)
v10 is spilled (because of the call to g), but the spill and reload are just 32-bit load/store.
On all architectures but PPC64, Trunc64to32 is a no-op. Why it is not on PPC64?
AMD64.rules:(Trunc64to32 x) -> x
ARM64.rules:(Trunc64to32 x) -> x
MIPS64.rules:(Trunc64to32 x) -> x
PPC64.rules:(Trunc64to32 x) && isSigned(x.Type) -> (MOVWreg x)
PPC64.rules:(Trunc64to32 x) -> (MOVWZreg x)
S390X.rules:(Trunc64to32 x) -> x
Wasm.rules:(Trunc64to(32|16|8) x) -> x
That was an old comment and I don't know where it originated. The rules beneath the comment are not mixing zero truncates with signed loads as this case does. I think more likely it has to do with the Trunc rules immediately above that comment, I think there was a concern about the change I made there.
On Fri, Jan 25, 2019 at 4:09 PM cherrymui notifications@github.com wrote:
The comment at https://go-review.googlesource.com/c/go/+/129875/8/src/cmd/compile/internal/ssa/gen/PPC64.rules#903 says
// Note that MOV??reg returns a 64-bit int, x is not necessarily that wide // This may interact with other patterns in the future. (Compare with arm64)
I think this is exactly the reason of this failure (yah, the "future" has come).
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/golang/go/issues/29943#issuecomment-457748135, or mute the thread https://github.com/notifications/unsubscribe-auth/AI_wjDe9X4LqVj3LBCsiORxNViN3cWw3ks5vG4CYgaJpZM4aTsXs .
I don't understand these rules (but I'm not familiar with this code). Truncating a 64-bit value to a 32-bit value is an operation that is independent of whether the type of the operand is signed.
It is not the rule immediately followed the comment. It is this
(MOVWZreg y:(MOVWZreg _)) -> y // repeat
The outer MOVWZ is ZeroExt32to64, the inner one is Trunc64to32. The reason is same: y
's type is not wide enough.
On ARM64, MIPS64, and S390X, it is (MOVWUreg x:(MOVWUreg _)) -> (MOVDreg x)
, to ensure that we don't narrow the type of the outer op.
What is our path forward here so that we can unblock the 1.12 release? Should we roll back this CL, or part of it?
Change https://golang.org/cl/159760 mentions this issue: cmd/compile: base PPC64 trunc rules on final type, not op type
This file is miscompiled on ppc64le:
On amd64 with tip, or on ppc64le with Go 1.11.5, this program runs without error. On ppc64le GNU/Linux with tip, the program panics:
Using objdump to look at the executable, I see this:
The variable
i32
is stored at48(r1)
. At PC62200
, the value is loaded, but it is loaded with anlwa
instruction, which sign extends the value. This corresponds to the Go expressionuint64(uint32(i32))
, which should zero extend the value, presumably usinglwz
.This is a miscompilation of valid code so it is a release blocker for 1.12.
CC @randall77 @laboger