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

cmd/compile/internal/gc: esc.go -m=2 reports invalid sink pos for some nodes #26987

Closed
quasilyte opened this issue Aug 14, 2018 · 3 comments
Closed
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@quasilyte
Copy link
Contributor

cmd/compile/internal/gc: esc.go -m=2 reports invalid sink pos for some nodes

Given this code (extracted from $GOROOT/test/escape_param.go):

package example

var sink interface{}

func param1(p1, p2 *int) (*int, *int) {
	return p1, p2
}

func caller1() {
	i := 0
	j := 0
	sink, _ = param1(&i, &j)
}

Execute the command $ go tool compile -m=2 example.go.
The output on tip is (important bits are in bold):

example.go:5:6: can inline param1 as: func(*int, *int) (*int, *int) { return p1, p2 }
example.go:9:6: can inline caller1 as: func() { i := 0; j := 0; sink, _ = param1(&i, &j) }
example.go:12:18: inlining call to param1 func(*int, *int) (*int, *int) { return p1, p2 }
example.go:5:13: leaking param: p1 to result ~r2 level=0
example.go:5:13: 	from ~r2 (return) at example.go:6:2
example.go:5:17: leaking param: p2 to result ~r3 level=0
example.go:5:17: 	from ~r3 (return) at example.go:6:2
example.go:12:10: (*int)(~r2) escapes to heap
example.go:12:10: 	from sink (assign-pair) at example.go:3:5
example.go:12:19: &i escapes to heap
example.go:12:19: 	from p1 (assign-pair) at example.go:12:18
example.go:12:19: 	from ~r2 (assign-pair) at example.go:12:18
example.go:12:19: 	from (*int)(~r2) (interface-converted) at example.go:12:10
example.go:12:19: 	from sink (assign-pair) at example.go:3:5
example.go:10:2: moved to heap: i
example.go:12:23: caller1 &j does not escape

Note that the assignment target is reported to be sink (correct) and the location is example.go:3:5 (not correct). During flow graph construction, it's clear that the location is example.go:12:10.

Before describing what's the reason behind that, we can compare OAS2 handling with OAS.
Here is slightly modified example:

package example2

var sink interface{}

func caller1() {
	i := 0
	sink = &i
}

The output for -m2 is:

example2.go:5:6: can inline caller1 as: func() { i := 0; sink = &i }
example2.go:7:7: &i escapes to heap
example2.go:7:7: 	from sink (assigned to top level variable) at example2.go:7:7
example2.go:7:9: &i escapes to heap
example2.go:7:9: 	from &i (interface-converted) at example2.go:7:7
example2.go:7:9: 	from sink (assigned to top level variable) at example2.go:7:7
example2.go:6:2: moved to heap: i

Nevermind that it prints &i escapes to heap twice, that's not relevant for this issue.
What is important is that it points to example2.go:7:7, the location where the assignment actually happens, not to the line where sink is declared.

Would be good to be consistent here and print assignment location for OAS too.

The code that causes problem is this:

if n.List.Len() == n.Rlist.Len() {
	rs := n.Rlist.Slice()
	for i, n := range n.List.Slice() {
		e.escassignWhyWhere(n, rs[i], "assign-pair", n)
	}
}

Note that n is OAS2, and inner n is an element from LHS and it shadows outer n.
So, instead of marking "assign-pair" with assignment position, we annotate it with LHS[i] position.
90% sure this is wrong and that error probably comes from copy/paste error (other e.escassignWhyWhere calls use n parameter as well, but there is no for loop that shadows it).

So, the proposed solution is to pass proper where argument to escassignWhyWhere.

Fixed output is:

example.go:5:6: can inline param1 as: func(*int, *int) (*int, *int) { return p1, p2 }
example.go:9:6: can inline caller1 as: func() { i := 0; j := 0; sink, _ = param1(&i, &j) }
example.go:12:18: inlining call to param1 func(*int, *int) (*int, *int) { return p1, p2 }
example.go:5:13: leaking param: p1 to result ~r2 level=0
example.go:5:13: 	from ~r2 (return) at example.go:6:2
example.go:5:17: leaking param: p2 to result ~r3 level=0
example.go:5:17: 	from ~r3 (return) at example.go:6:2
example.go:12:10: (*int)(~r2) escapes to heap
example.go:12:10: 	from sink (assign-pair) at example.go:12:10
example.go:12:19: &i escapes to heap
example.go:12:19: 	from p1 (assign-pair) at example.go:12:18
example.go:12:19: 	from ~r2 (assign-pair) at example.go:12:18
example.go:12:19: 	from (*int)(~r2) (interface-converted) at example.go:12:10
example.go:12:19: 	from sink (assign-pair) at example.go:12:10
example.go:10:2: moved to heap: i
example.go:12:23: caller1 &j does not escape

Note that now it points to example.go:12:10.

I find old output confusing and inconsistent, hence I'm proposing fixing it.

@quasilyte
Copy link
Contributor Author

CL with a fix will be submitted soon.

@gopherbot
Copy link

Change https://golang.org/cl/129315 mentions this issue: cmd/compile/internal/gc: fix invalid positions for sink nodes in esc.go

@andybons andybons added the NeedsFix The path to resolution is known, but the work has not been done. label Aug 14, 2018
@andybons andybons added this to the Go1.11 milestone Aug 14, 2018
@josharian josharian modified the milestones: Go1.11, Go1.12 Aug 14, 2018
@josharian
Copy link
Contributor

Moved to 1.12; this is not critical.

@golang golang locked and limited conversation to collaborators Sep 3, 2019
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
Development

No branches or pull requests

4 participants