Skip to content

Commit

Permalink
Working on instance tracking for DCE
Browse files Browse the repository at this point in the history
  • Loading branch information
grantnelson-wf committed Aug 23, 2024
1 parent 3b94762 commit 8777c8e
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 16 deletions.
4 changes: 4 additions & 0 deletions compiler/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@ func (fc *funcContext) translateExpr(expr ast.Expr) *expression {
if typesutil.IsJsPackage(obj.Pkg()) && obj.Name() == "InternalObject" {
return fc.translateExpr(e.Args[0])
}

if inst, has := fc.pkgCtx.Instances[f]; has {
fc.pkgCtx.DeclareDCEDepWithInstance(obj, inst)
}
return fc.translateCall(e, sig, fc.translateExpr(f))

case *ast.SelectorExpr:
Expand Down
43 changes: 40 additions & 3 deletions compiler/internal/dce/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package dce
import (
"errors"
"go/types"
"strings"

"github.com/gopherjs/gopherjs/compiler/typesutil"
)

// Decl is any code declaration that has dead-code elimination (DCE)
Expand All @@ -14,7 +17,7 @@ type Decl interface {
// Collector is a tool to collect dependencies for a declaration
// that'll be used in dead-code elimination (DCE).
type Collector struct {
dependencies map[types.Object]struct{}
dependencies map[string]struct{}
}

// CollectDCEDeps captures a list of Go objects (types, functions, etc.)
Expand All @@ -28,7 +31,7 @@ func (c *Collector) CollectDCEDeps(decl Decl, f func()) {
panic(errors.New(`called CollectDCEDeps inside another CollectDCEDeps call`))
}

c.dependencies = make(map[types.Object]struct{})
c.dependencies = make(map[string]struct{})
defer func() { c.dependencies = nil }()

f()
Expand All @@ -39,8 +42,42 @@ func (c *Collector) CollectDCEDeps(decl Decl, f func()) {
// DeclareDCEDep records that the code that is currently being transpiled
// depends on a given Go object.
func (c *Collector) DeclareDCEDep(o types.Object) {
c.DeclareDCEDepWithInstance(o, types.Instance{})
}

func (c *Collector) DeclareDCEDepWithInstance(o types.Object, inst types.Instance) {
if c.dependencies == nil {
return // Dependencies are not being collected.
}
c.dependencies[o] = struct{}{}

// add object dependency
// If the object is a type it will be added as <path>.<name> (e.g. "foo.Bar")
// If the object is a method (with a receiver) have a tilde added (e.g. "foo.Bar~")
// If the object is an instance of a generic, the type arguments are
// added (e.g. "foo.Bar[int, string]" or "foo.Bar[int, string]~").
//
// The methods are set as dependencies without their receiver type since
// the DCE doesn't track if the method is part of an interface or a concrete type.
// This means that if a method is used, all types that have this method will be kept
// since the method is not tied to a specific type and maybe used via an interface.
qualifiedName := o.Pkg().Path() + "." + o.Name()
if inst.TypeArgs != nil {
tps := make([]string, inst.TypeArgs.Len())
for i := range tps {
tps[i] = inst.TypeArgs.At(i).String()
}
qualifiedName += `[` + strings.Join(tps, `, `) + `]`
}
if typesutil.IsMethod(o) {
qualifiedName += "~"
}
c.dependencies[qualifiedName] = struct{}{}

// TODO(gn): Things to test:
// - Casting to type (e.g. `type X[T any] interface{ Get() T }; struct Y{}; func (y Y) Get() int { return 42 }; var v X[int] = Y{}`)
// - Complex type parameters (e.g. `type X[T any] struct{v T}; func (x X[T]) Get() T { return v }; var v = X[X[X[int]]]{v: X[X[int]]{v: X[int]{v: 42}}}.Get().Get().Get()`)
// - Automatic type selection (e.g. Sum[T int | float64](values ...T) T { ** }; var vi = Sum(42, 43), vf = Sum(3.14, 4.25)`)
// - Embedded instance in type (e.g. `type X[T any] struct{v T}; type Y[T any] struct{X[T]}; var v = Y[int]{X: X[int]{v: 42}}.X.v`)
// - Embedded interface in struct providing the type with an exposed method.
// - Type params that are self referencing (e.g. `func Keys[K comparable, V any, M ~map[K]V](m M) { ** }`)
}
5 changes: 3 additions & 2 deletions compiler/internal/dce/dce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,9 @@ func Test_Info_SetNameAndDep(t *testing.T) {
d := &testDecl{}
t.Log(`object:`, types.ObjectString(tt.obj, nil))

d.Dce().setDeps(map[types.Object]struct{}{
tt.obj: {},
c := Collector{}
c.CollectDCEDeps(d, func() {
c.DeclareDCEDep(tt.obj)
})
equal(t, len(d.Dce().deps), 1)
equal(t, d.Dce().deps[0], tt.wantDep)
Expand Down
17 changes: 9 additions & 8 deletions compiler/internal/dce/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,16 @@ func (d *Info) SetName(o types.Object) {
// setDeps sets the declaration dependencies used by DCE
// for the declaration this DCE info is attached to.
// This overwrites any prior set dependencies.
func (d *Info) setDeps(objectSet map[types.Object]struct{}) {
deps := make([]string, 0, len(objectSet))
for o := range objectSet {
qualifiedName := o.Pkg().Path() + "." + o.Name()
if typesutil.IsMethod(o) {
qualifiedName += "~"
}
deps = append(deps, qualifiedName)
func (d *Info) setDeps(depSet map[string]struct{}) {
deps := make([]string, 0, len(depSet))
for dep := range depSet {
deps = append(deps, dep)
}
sort.Strings(deps)
d.deps = deps

// TODO(gn): REMOVE
if d.importPath == `main` {
fmt.Printf(">setDeps: %s\n", d.String())
}
}
9 changes: 7 additions & 2 deletions compiler/statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,14 @@ func (fc *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) {
}
case token.TYPE:
for _, spec := range decl.Specs {
o := fc.pkgCtx.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName)
id := spec.(*ast.TypeSpec).Name
o := fc.pkgCtx.Defs[id].(*types.TypeName)
fc.pkgCtx.typeNames.Add(o)
fc.pkgCtx.DeclareDCEDep(o)
if inst, has := fc.pkgCtx.Instances[id]; has {
fc.pkgCtx.DeclareDCEDepWithInstance(o, inst)
} else {
fc.pkgCtx.DeclareDCEDep(o)
}
}
case token.CONST:
// skip, constants are inlined
Expand Down
2 changes: 1 addition & 1 deletion compiler/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ func (fc *funcContext) typeName(ty types.Type) string {
}

// For anonymous composite types, generate a synthetic package-level type
// declaration, which will be reused for all instances of this time. This
// declaration, which will be reused for all instances of this type. This
// improves performance, since runtime won't have to synthesize the same type
// repeatedly.
anonType, ok := fc.pkgCtx.anonTypeMap.At(ty).(*types.TypeName)
Expand Down

0 comments on commit 8777c8e

Please sign in to comment.