]> Cypherpunks repositories - gostls13.git/commit
cmd/link: prune unused methods
authorDavid Crawshaw <crawshaw@golang.org>
Tue, 8 Mar 2016 04:45:04 +0000 (23:45 -0500)
committerDavid Crawshaw <crawshaw@golang.org>
Wed, 9 Mar 2016 22:22:46 +0000 (22:22 +0000)
commit862b9ddda7d5163926ca0ab20f23f261a70cfe80
tree13597055178a98a78bb0b01cb4d6121cae48f2ac
parentf02dc513c823eff47b695ba0652ac6f20621beba
cmd/link: prune unused methods

Today the linker keeps all methods of reachable types. This is
necessary if a program uses reflect.Value.Call. But while use of
reflection is widespread in Go for encoders and decoders, using
it to call a method is rare.

This CL looks for the use of reflect.Value.Call in a program, and
if it is absent, adopts a (reasonably conservative) method pruning
strategy as part of dead code elimination. Any method that is
directly called is kept, and any method that matches a used
interface's method signature is kept.

Whether or not a method body is kept is determined by the relocation
from its receiver's *rtype to its *rtype. A small change in the
compiler marks these relocations as R_METHOD so they can be easily
collected and manipulated by the linker.

As a bonus, this technique removes the text segment of methods that
have been inlined. Looking at the output of building cmd/objdump with
-ldflags=-v=2 shows that inlined methods like
runtime.(*traceAllocBlockPtr).ptr are removed from the program.

Relatively little work is necessary to do this. Linking two
examples, jujud and cmd/objdump show no more than +2% link time.

Binaries that do not use reflect.Call.Value drop 4 - 20% in size:

addr2line: -793KB (18%)
asm:       -346KB (8%)
cgo:       -490KB (10%)
compile:   -564KB (4%)
dist:      -736KB (17%)
fix:       -404KB (12%)
link:      -328KB (7%)
nm:        -827KB (19%)
objdump:   -712KB (16%)
pack:      -327KB (14%)
yacc:      -350KB (10%)

Binaries that do use reflect.Call.Value see a modest size decrease
of 2 - 6% thanks to pruning of unexported methods:

api:    -151KB (3%)
cover:  -222KB (4%)
doc:    -106KB (2.5%)
pprof:  -314KB (3%)
trace:  -357KB (4%)
vet:    -187KB (2.7%)
jujud:  -4.4MB (5.8%)
cmd/go: -384KB (3.4%)

The trivial Hello example program goes from 2MB to 1.68MB:

package main

import "fmt"

func main() {
fmt.Println("Hello, 世界")
}

Method pruning also helps when building small binaries with
"-ldflags=-s -w". The above program goes from 1.43MB to 1.2MB.

Unfortunately the linker can only tell if reflect.Value.Call has been
statically linked, not if it is dynamically used. And while use is
rare, it is linked into a very common standard library package,
text/template. The result is programs like cmd/go, which don't use
reflect.Value.Call, see limited benefit from this CL. If binary size
is important enough it may be possible to address this in future work.

For #6853.

Change-Id: Iabe90e210e813b08c3f8fd605f841f0458973396
Reviewed-on: https://go-review.googlesource.com/20483
Reviewed-by: Russ Cox <rsc@golang.org>
src/cmd/compile/internal/gc/reflect.go
src/cmd/internal/obj/link.go
src/cmd/link/internal/ld/deadcode.go [new file with mode: 0644]
src/cmd/link/internal/ld/decodesym.go
src/cmd/link/internal/ld/go.go
src/cmd/link/internal/ld/pobj.go