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: missed devirtualization #51554

Open
CAFxX opened this issue Mar 9, 2022 · 1 comment
Open

cmd/compile: missed devirtualization #51554

CAFxX opened this issue Mar 9, 2022 · 1 comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Performance
Milestone

Comments

@CAFxX
Copy link
Contributor

CAFxX commented Mar 9, 2022

In the following snippet, the compiler should be able to devirtualize the fi.Foo() call, but currently this does not happen (godbolt):

package main

import "fmt"

type foo struct{}

func (_ foo) Foo() { fmt.Println("Foo") }

func main() {
	f := foo{}
	switch fi := any(f).(type) {
	case interface{ Foo() }:
		fi.Foo()
	}
}

The example is obviously artificial, but this definitely occurs in real code: e.g. see the code generated by envoyproxy/protoc-gen-validate, e.g. https://github.com/envoyproxy/go-control-plane/blob/99c9ceb26c9d7bf1705833b7debdebe9ae67c0ae/envoy/data/tap/v2alpha/transport.pb.validate.go.

Another use case that will become increasingly important with generics in 1.18 is to distinguish1 between different types provided in type unions, e.g.:

func Foo[Bytes interface{ []byte | string }](b Bytes) string {
	switch any(b).(type) {
	case []byte:
		return "[]byte"
	case string:
		return "string"
	}
    return "<unknown>"
}

As above, this missed devirtualization opportunity currently prevents (godbolt) the compiler from statically deducing the correct branch at compile-time, and pruning the dead branches.

This is somewhat related to #38992 in that also in this specific case it would be beneficial to run inlining again after devirtualization. For example, in the first example above, inlining after devirtualization would open up significant inlining opportunities in case an embedded message does not have validation rules (in which case its Validate* functions would simply return nil, potentially allowing to prune whole branches of the validation call tree)

Footnotes

  1. AFAICT this wouldn't currently be possible if the type constraint includes approximation elements (~T); the example does not use approximation elements for this reason

@ALTree ALTree added this to the Unplanned milestone Mar 9, 2022
@ALTree ALTree added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Mar 9, 2022
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jul 13, 2022
@AlekseyCherepanov
Copy link

This optimization is important to make efficient generic algorithms where parameterized part is called in a hot loop. For instance, sort.Sort and sort.Interface: method Less is called in every iteration. Sort might be implemented with generic types, but then method Less would be called anyway instead of inlining due to this missing optimization. (I tried some bits on 1.19.3 and 1.20rc3.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Performance
Projects
None yet
Development

No branches or pull requests

4 participants