Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gollvm/gofrontend: when evaluates x twice in "x op= y", which was detectable if evaluating y affects x. #52811

Closed
xqq0629 opened this issue May 10, 2022 · 13 comments · May be fixed by golang/gofrontend#7 or golang/gofrontend#8
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@xqq0629
Copy link

xqq0629 commented May 10, 2022

What version of Go are you using (go version)?

$ go version

gollvm

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env


Debian
Intel(R) Xeon(R) Platinum 8260 CPU @ 2.40GHz (Cascade Lake)

What did you do?

error case:

package main
import "fmt"
func mapscase() int {
    one := map[int]int{0: 1299}
    two := map[int]int{0: 2388}

    x := one
    x[0] += func() int {
        x = two
        return 1113
    }()

    return two[0]
}

func main() {
    a := mapscase()
    fmt.Println(a)
}

What did you expect to see?

3501

What did you see instead?

2412
cmd/compile: "x[i] op= y" evaluates x[i] more than once can also be referenced.

Error & Solution

An output of 2412 indicates that x[0] was evaluated twice as follows: once indexing into the old map to read the value 1299, and then once into the new to store the value 1299 + 1113.

gofrontend doesn't consider such case: when evaluates x twice in "x op= y", evaluating y may affect x. In such case, y on the right should be evaluated first and x on the left should be read latter.

@xqq0629 xqq0629 changed the title gollvm/gofrontend: gollvm/gofrontend: when evaluates x twice in "x op= y", which was detectable if evaluating y affects x. May 10, 2022
xqq0629 pushed a commit to xqq0629/gofrontend that referenced this issue May 10, 2022
when evaluates x twice in "x op= y", which was detectable if
evaluating y affects x. We should identify such cases and evaluate
y first.

fix golang/go#52811 (golang/go#52811)
tsqua added a commit to tsqua/gofrontend that referenced this issue May 10, 2022
When evaluates x twice in "x op= y", which was detectable if
evaluating y affects x. We should identify such cases and evaluate
y first.

For reference see this bug in the go git repository:

Fix golang/go#52811
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/405394 mentions this issue: fix when "x[i] op= y" evaluates x[i] more than once

@heschi heschi added the NeedsFix The path to resolution is known, but the work has not been done. label May 10, 2022
@heschi heschi added this to the Unreleased milestone May 10, 2022
@heschi
Copy link
Contributor

heschi commented May 10, 2022

cc @thanm

@ianlancetaylor
Copy link
Contributor

I don't think that the language spec provides any guarantees about the order of evaluation of this statement. If you think that it does guarantee the order, can you explain your reasoning? Thanks.

CC @griesemer @mdempsky

@ianlancetaylor ianlancetaylor added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label May 10, 2022
@ianlancetaylor ianlancetaylor modified the milestones: Unreleased, Gccgo May 10, 2022
@gopherbot gopherbot removed the NeedsFix The path to resolution is known, but the work has not been done. label May 10, 2022
@mdempsky
Copy link
Contributor

I think the issue is valid. Clearer repro:

package main

func main() {
	one := map[int]string{0: "one"}
	two := map[int]string{0: "two"}

	x := one
	x[0] += func() string {
		x = two
		return ""
	}()

	println(one[0], two[0])
}

This program should print one two, but with "gccgo (Debian 11.2.0-16+build1) 11.2.0" it prints one one.

@mdempsky
Copy link
Contributor

Rationale: the x in x[0] += ... should only be evaluated once. It's unspecified whether x is evaluated before or after the RHS function call, so x may evaluate to either map. But whichever map it evaluates to, we should load from and store back to the same map.

Printing one one demonstrates that gccgo is loading from the first map, and then writing back to the second map. That's incorrect.

@griesemer
Copy link
Contributor

I would agree with that. Per the spec

The assignment proceeds in two phases. First, the operands of index expressions and pointer indirections (including implicit pointer indirections in selectors) on the left and the expressions on the right are all evaluated in the usual order. Second, the assignments are carried out in left-to-right order.

So the operand x of x[0], should be evaluated at most once.

@mdempsky mdempsky added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels May 10, 2022
@ianlancetaylor
Copy link
Contributor

OK, thanks.

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/405616 mentions this issue: test: add test that gofrontend miscompiles

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/405617 mentions this issue: compiler: load LHS subexpressions of op= assignment only once

@huanglanzhiguan
Copy link

We found this issue by testing GoLLVM using the test case:
fixedbugs/issue21687.go

@mdempsky
Copy link
Contributor

@huanglanzhiguan Thanks. That would have been a helpful detail to mention in the original issue report.

@ianlancetaylor
Copy link
Contributor

I've written a patch for the gofrontend to attempt to fix this problem (https://go.dev/cl/405617). When I run the initial program in this issue with the gc compiler, it prints 3501. When I run it with the patched gofrontend, I get 2388.

This hinges on how we execute

    x[0] += func() int {
        x = two
        return 1113
    }()

I'm currently executing it as

    t1 := x
    t2 := 0
    t3 := func() int { x = two; return 1113 }()
    t1[t2] = t3

When I do that, the program prints 2388.

The only way to get 3501 is for the function to be executed before loading x. I don't see why that is mandatory.

Does anybody think that the gofrontend is incorrect in printing 2388? Does anybody think that gc is incorrect in printing 3501? Thanks.

@mdempsky
Copy link
Contributor

It's correct for the reported, obfuscated test case to print either 2388 or 3501 (2388+1113). It's just incorrect to print other values, including 2412 (1299+1113).

The original issue21687.go test case was written specifically to be flexible about order-of-evaluation variations in compilers.

The new gofrontend behavior sounds correct to me.

realqhc pushed a commit to realqhc/gofrontend that referenced this issue Aug 4, 2022
Fixes golang/go#52811

Change-Id: I369415d1188a483cba81257fdfbb0e5c1a7df1c4
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/405617
Reviewed-by: Cherry Mui <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
@golang golang locked and limited conversation to collaborators May 17, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
7 participants