Skip to content

Commit

Permalink
cmd/link: don't mark a symbol's Gotype reachable
Browse files Browse the repository at this point in the history
A symbol being reachable doesn't imply its type descriptor is
needed. Don't mark it.

If the type is converted to interface somewhere in the program,
there will be an explicit use of the type descriptor, which
will make it marked.

A println("hello") program before and after

-rwxr-xr-x  1 cherryyz  primarygroup  1259824 Apr 30 23:00 hello
-rwxr-xr-x  1 cherryyz  primarygroup  1169680 Apr 30 23:10 hello

Updates #38782.
Updates #6853.

Change-Id: I88884c126ce75ba073f1ba059c4b892c87d2ac96
Reviewed-on: https://go-review.googlesource.com/c/go/+/231397
Run-TryBot: Cherry Zhang <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Alessandro Arzilli <[email protected]>
Reviewed-by: Matthew Dempsky <[email protected]>
  • Loading branch information
cherrymui committed May 1, 2020
1 parent 3cec330 commit 44d2286
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 34 deletions.
11 changes: 10 additions & 1 deletion src/cmd/link/internal/ld/deadcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package ld

import (
"bytes"
"cmd/internal/goobj2"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
Expand Down Expand Up @@ -154,7 +155,15 @@ func (d *deadcodePass) flood() {
}
naux := d.ldr.NAux(symIdx)
for i := 0; i < naux; i++ {
d.mark(d.ldr.Aux2(symIdx, i).Sym(), symIdx)
a := d.ldr.Aux2(symIdx, i)
if a.Type() == goobj2.AuxGotype && !d.ctxt.linkShared {
// A symbol being reachable doesn't imply we need its
// type descriptor. Don't mark it.
// XXX we need that for GCProg generation when linking
// shared library. why?
continue
}
d.mark(a.Sym(), symIdx)
}
// Some host object symbols have an outer object, which acts like a
// "carrier" symbol, or it holds all the symbols for a particular
Expand Down
54 changes: 21 additions & 33 deletions src/cmd/link/internal/ld/deadcode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,48 +14,36 @@ import (
"testing"
)

// This example uses reflect.Value.Call, but not
// reflect.{Value,Type}.Method. This should not
// need to bring all methods live.
const deadcodeTestSrc = `
package main
import "reflect"
func f() { println("call") }
type T int
func (T) M() {}
func main() {
v := reflect.ValueOf(f)
v.Call(nil)
i := interface{}(T(1))
println(i)
}
`

func TestDeadcode(t *testing.T) {
testenv.MustHaveGoBuild(t)
t.Parallel()

tmpdir, err := ioutil.TempDir("", "TestDeadcode")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)

src := filepath.Join(tmpdir, "main.go")
err = ioutil.WriteFile(src, []byte(deadcodeTestSrc), 0666)
if err != nil {
t.Fatal(err)
}
exe := filepath.Join(tmpdir, "main.exe")

cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-dumpdep", "-o", exe, src)
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
tests := []struct {
src string
pattern string
}{
{"reflectcall", "main.T.M"},
{"typedesc", "type.main.T"},
}
if bytes.Contains(out, []byte("main.T.M")) {
t.Errorf("main.T.M should not be reachable. Output:\n%s", out)
for _, test := range tests {
t.Run(test.src, func(t *testing.T) {
t.Parallel()
src := filepath.Join("testdata", "deadcode", test.src+".go")
exe := filepath.Join(tmpdir, test.src+".exe")
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-dumpdep", "-o", exe, src)
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
}
if bytes.Contains(out, []byte(test.pattern)) {
t.Errorf("%s should not be reachable. Output:\n%s", test.pattern, out)
}
})
}
}
24 changes: 24 additions & 0 deletions src/cmd/link/internal/ld/testdata/deadcode/reflectcall.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This example uses reflect.Value.Call, but not
// reflect.{Value,Type}.Method. This should not
// need to bring all methods live.

package main

import "reflect"

func f() { println("call") }

type T int

func (T) M() {}

func main() {
v := reflect.ValueOf(f)
v.Call(nil)
i := interface{}(T(1))
println(i)
}
16 changes: 16 additions & 0 deletions src/cmd/link/internal/ld/testdata/deadcode/typedesc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Test that a live variable doesn't bring its type
// descriptor live.

package main

type T [10]string

var t T

func main() {
println(t[8])
}

0 comments on commit 44d2286

Please sign in to comment.