Skip to content

Commit 44d2286

Browse files
committed
cmd/link: don't mark a symbol's Gotype reachable
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]>
1 parent 3cec330 commit 44d2286

File tree

4 files changed

+71
-34
lines changed

4 files changed

+71
-34
lines changed

src/cmd/link/internal/ld/deadcode.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package ld
66

77
import (
88
"bytes"
9+
"cmd/internal/goobj2"
910
"cmd/internal/objabi"
1011
"cmd/internal/sys"
1112
"cmd/link/internal/loader"
@@ -154,7 +155,15 @@ func (d *deadcodePass) flood() {
154155
}
155156
naux := d.ldr.NAux(symIdx)
156157
for i := 0; i < naux; i++ {
157-
d.mark(d.ldr.Aux2(symIdx, i).Sym(), symIdx)
158+
a := d.ldr.Aux2(symIdx, i)
159+
if a.Type() == goobj2.AuxGotype && !d.ctxt.linkShared {
160+
// A symbol being reachable doesn't imply we need its
161+
// type descriptor. Don't mark it.
162+
// XXX we need that for GCProg generation when linking
163+
// shared library. why?
164+
continue
165+
}
166+
d.mark(a.Sym(), symIdx)
158167
}
159168
// Some host object symbols have an outer object, which acts like a
160169
// "carrier" symbol, or it holds all the symbols for a particular

src/cmd/link/internal/ld/deadcode_test.go

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,36 @@ import (
1414
"testing"
1515
)
1616

17-
// This example uses reflect.Value.Call, but not
18-
// reflect.{Value,Type}.Method. This should not
19-
// need to bring all methods live.
20-
const deadcodeTestSrc = `
21-
package main
22-
import "reflect"
23-
24-
func f() { println("call") }
25-
26-
type T int
27-
func (T) M() {}
28-
29-
func main() {
30-
v := reflect.ValueOf(f)
31-
v.Call(nil)
32-
i := interface{}(T(1))
33-
println(i)
34-
}
35-
`
36-
3717
func TestDeadcode(t *testing.T) {
3818
testenv.MustHaveGoBuild(t)
19+
t.Parallel()
3920

4021
tmpdir, err := ioutil.TempDir("", "TestDeadcode")
4122
if err != nil {
4223
t.Fatal(err)
4324
}
4425
defer os.RemoveAll(tmpdir)
4526

46-
src := filepath.Join(tmpdir, "main.go")
47-
err = ioutil.WriteFile(src, []byte(deadcodeTestSrc), 0666)
48-
if err != nil {
49-
t.Fatal(err)
50-
}
51-
exe := filepath.Join(tmpdir, "main.exe")
52-
53-
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-dumpdep", "-o", exe, src)
54-
out, err := cmd.CombinedOutput()
55-
if err != nil {
56-
t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
27+
tests := []struct {
28+
src string
29+
pattern string
30+
}{
31+
{"reflectcall", "main.T.M"},
32+
{"typedesc", "type.main.T"},
5733
}
58-
if bytes.Contains(out, []byte("main.T.M")) {
59-
t.Errorf("main.T.M should not be reachable. Output:\n%s", out)
34+
for _, test := range tests {
35+
t.Run(test.src, func(t *testing.T) {
36+
t.Parallel()
37+
src := filepath.Join("testdata", "deadcode", test.src+".go")
38+
exe := filepath.Join(tmpdir, test.src+".exe")
39+
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-dumpdep", "-o", exe, src)
40+
out, err := cmd.CombinedOutput()
41+
if err != nil {
42+
t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
43+
}
44+
if bytes.Contains(out, []byte(test.pattern)) {
45+
t.Errorf("%s should not be reachable. Output:\n%s", test.pattern, out)
46+
}
47+
})
6048
}
6149
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// This example uses reflect.Value.Call, but not
6+
// reflect.{Value,Type}.Method. This should not
7+
// need to bring all methods live.
8+
9+
package main
10+
11+
import "reflect"
12+
13+
func f() { println("call") }
14+
15+
type T int
16+
17+
func (T) M() {}
18+
19+
func main() {
20+
v := reflect.ValueOf(f)
21+
v.Call(nil)
22+
i := interface{}(T(1))
23+
println(i)
24+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Test that a live variable doesn't bring its type
6+
// descriptor live.
7+
8+
package main
9+
10+
type T [10]string
11+
12+
var t T
13+
14+
func main() {
15+
println(t[8])
16+
}

0 commit comments

Comments
 (0)