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: escape analysis of interface calls to non-exported method names #16001

Open
mdempsky opened this issue Jun 8, 2016 · 2 comments
Milestone

Comments

@mdempsky
Copy link
Member

@mdempsky mdempsky commented Jun 8, 2016

Consider this Go source:

package p

import "fmt"

type A int
func (A) f(x *int) { fmt.Println("A", *x) }

type B int
func (B) f(x *int) { fmt.Println("B", *x) }
func (B) g() {}

type Fer interface { f(x *int); g() }

func H(f Fer) {
    i := 42
    f.f(&i)
}

At the f.f(&i) call site in H, we can't be sure exactly what method will be called. However, since it's an unexported method name, it must be implemented by a method in the same package. Consequently, if we know that all candidate methods in the package are non-escaping (such as A.f and B.f are), we can still be sure that f.f(&i) is a non-escaping call.

Note that we still have to consider A.f even though A itself doesn't implement Fer, because a downstream package could do something like:

package q

import "p"

type X struct { p.B }
type Y struct { p.A; X }

func Z() {
    p.H(Y{})
}

There are probably refinements that could avoid this though.

I happened to notice this because of package net's "exchange" function, which calls the unexported TCPConn.dnsRoundTrip or UDPConn.dnsRoundTrip methods. I haven't investigated how common this situation is though.

/cc @dr2chase @alandonovan

@mdempsky mdempsky added this to the Unplanned milestone Jun 8, 2016
@mdempsky mdempsky changed the title cmd/compile: escape analysis of interface calls to unexported method names cmd/compile: escape analysis of interface calls to non-exported method names Sep 3, 2019
@mdempsky

This comment has been minimized.

Copy link
Member Author

@mdempsky mdempsky commented Sep 3, 2019

The motivating example of {TCP,UDP}Conn.dnsRoundTrip is no longer relevant (it seems to have been refactored away), but I think this is still desirable and likely easier to do now.

I think the main steps here are:

  1. During typecheck, keep track of each non-exported method in a map keyed by (name, signature).
  2. During visitBottomUp, recognize that OCALLINTER calls to a non-exported method have a (conservative) dependency on all of the methods associated with that same (name, signature).
  3. During escape analysis, treat an OCALLINTER as a call to each of those associated methods.
@mdempsky

This comment has been minimized.

Copy link
Member Author

@mdempsky mdempsky commented Sep 12, 2019

Started playing with this idea more. It turns out there are 263 call sites within the Go repo that invoke a non-exported method through an interface receiver. So this probably is worth exploring further.

Also, at least github.com/ianlancetaylor/demangle/ast.go has a pattern like:

type I interface { print(*t) }
func (T1) print() { I.print(...) }
func (T2) print() { I.print(...) }
...
func (Tk) print() { I.print(...) }

Directly treating each of the I.print calls as a call to each of the Tx.print methods would involve O(N^2) flow edges.

It's probably better to create a pseudo-function for I.print so that we only need O(N) flow edges from all of the call sites to I.print, and then O(N) flow edges from I.print to each of the Tx.print methods.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant
You can’t perform that action at this time.