github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/link/internal/ld/deadcode.go (about)

     1  // Copyright 2016 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  package ld
     6  
     7  import (
     8  	"cmd/internal/obj"
     9  	"fmt"
    10  	"strings"
    11  	"unicode"
    12  )
    13  
    14  // deadcode marks all reachable symbols.
    15  //
    16  // The basis of the dead code elimination is a flood fill of symbols,
    17  // following their relocations, beginning at INITENTRY.
    18  //
    19  // This flood fill is wrapped in logic for pruning unused methods.
    20  // All methods are mentioned by relocations on their receiver's *rtype.
    21  // These relocations are specially defined as R_METHOD by the compiler
    22  // so we can detect and manipulated them here.
    23  //
    24  // There are three ways a method of a reachable type can be invoked:
    25  //
    26  //	1. direct call
    27  //	2. through a reachable interface type
    28  //	3. reflect.Value.Call, .Method, or reflect.Method.Func
    29  //
    30  // The first case is handled by the flood fill, a directly called method
    31  // is marked as reachable.
    32  //
    33  // The second case is handled by decomposing all reachable interface
    34  // types into method signatures. Each encountered method is compared
    35  // against the interface method signatures, if it matches it is marked
    36  // as reachable. This is extremely conservative, but easy and correct.
    37  //
    38  // The third case is handled by looking to see if any of:
    39  //	- reflect.Value.Call is reachable
    40  //	- reflect.Value.Method is reachable
    41  // 	- reflect.Type.Method or MethodByName is called.
    42  // If any of these happen, all bets are off and all exported methods
    43  // of reachable types are marked reachable.
    44  //
    45  // Any unreached text symbols are removed from ctxt.Textp.
    46  func deadcode(ctxt *Link) {
    47  	if Debug['v'] != 0 {
    48  		fmt.Fprintf(ctxt.Bso, "%5.2f deadcode\n", obj.Cputime())
    49  	}
    50  
    51  	d := &deadcodepass{
    52  		ctxt:        ctxt,
    53  		ifaceMethod: make(map[methodsig]bool),
    54  	}
    55  
    56  	// First, flood fill any symbols directly reachable in the call
    57  	// graph from INITENTRY. Ignore all methods not directly called.
    58  	d.init()
    59  	d.flood()
    60  
    61  	callSym := Linkrlookup(ctxt, "reflect.Value.Call", 0)
    62  	methSym := Linkrlookup(ctxt, "reflect.Value.Method", 0)
    63  	reflectSeen := false
    64  
    65  	if DynlinkingGo() {
    66  		// Exported methods may satisfy interfaces we don't know
    67  		// about yet when dynamically linking.
    68  		reflectSeen = true
    69  	}
    70  
    71  	for {
    72  		if !reflectSeen {
    73  			if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) {
    74  				// Methods might be called via reflection. Give up on
    75  				// static analysis, mark all exported methods of
    76  				// all reachable types as reachable.
    77  				reflectSeen = true
    78  			}
    79  		}
    80  
    81  		// Mark all methods that could satisfy a discovered
    82  		// interface as reachable. We recheck old marked interfaces
    83  		// as new types (with new methods) may have been discovered
    84  		// in the last pass.
    85  		var rem []methodref
    86  		for _, m := range d.markableMethods {
    87  			if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
    88  				d.markMethod(m)
    89  			} else {
    90  				rem = append(rem, m)
    91  			}
    92  		}
    93  		d.markableMethods = rem
    94  
    95  		if len(d.markQueue) == 0 {
    96  			// No new work was discovered. Done.
    97  			break
    98  		}
    99  		d.flood()
   100  	}
   101  
   102  	// Remove all remaining unreached R_METHOD relocations.
   103  	for _, m := range d.markableMethods {
   104  		for _, r := range m.r {
   105  			d.cleanupReloc(r)
   106  		}
   107  	}
   108  
   109  	if Buildmode != BuildmodeShared {
   110  		// Keep a typelink or itablink if the symbol it points at is being kept.
   111  		// (When BuildmodeShared, always keep typelinks and itablinks.)
   112  		for _, s := range ctxt.Allsym {
   113  			if strings.HasPrefix(s.Name, "go.typelink.") ||
   114  				strings.HasPrefix(s.Name, "go.itablink.") {
   115  				s.Attr.Set(AttrReachable, len(s.R) == 1 && s.R[0].Sym.Attr.Reachable())
   116  			}
   117  		}
   118  	}
   119  
   120  	// Remove dead text but keep file information (z symbols).
   121  	var last *LSym
   122  	for s := ctxt.Textp; s != nil; s = s.Next {
   123  		if !s.Attr.Reachable() {
   124  			continue
   125  		}
   126  		if last == nil {
   127  			ctxt.Textp = s
   128  		} else {
   129  			last.Next = s
   130  		}
   131  		last = s
   132  	}
   133  	if last == nil {
   134  		ctxt.Textp = nil
   135  		ctxt.Etextp = nil
   136  	} else {
   137  		last.Next = nil
   138  		ctxt.Etextp = last
   139  	}
   140  }
   141  
   142  var markextra = []string{
   143  	"runtime.morestack",
   144  	"runtime.morestackx",
   145  	"runtime.morestack00",
   146  	"runtime.morestack10",
   147  	"runtime.morestack01",
   148  	"runtime.morestack11",
   149  	"runtime.morestack8",
   150  	"runtime.morestack16",
   151  	"runtime.morestack24",
   152  	"runtime.morestack32",
   153  	"runtime.morestack40",
   154  	"runtime.morestack48",
   155  
   156  	// on arm, lock in the div/mod helpers too
   157  	"_div",
   158  	"_divu",
   159  	"_mod",
   160  	"_modu",
   161  }
   162  
   163  // methodref holds the relocations from a receiver type symbol to its
   164  // method. There are three relocations, one for each of the fields in
   165  // the reflect.method struct: mtyp, ifn, and tfn.
   166  type methodref struct {
   167  	m   methodsig
   168  	src *LSym     // receiver type symbol
   169  	r   [3]*Reloc // R_METHOD relocations to fields of runtime.method
   170  }
   171  
   172  func (m methodref) mtyp() *LSym { return m.r[0].Sym }
   173  func (m methodref) ifn() *LSym  { return m.r[1].Sym }
   174  func (m methodref) tfn() *LSym  { return m.r[2].Sym }
   175  
   176  func (m methodref) isExported() bool {
   177  	for _, r := range m.m {
   178  		return unicode.IsUpper(r)
   179  	}
   180  	panic("methodref has no signature")
   181  }
   182  
   183  // deadcodepass holds state for the deadcode flood fill.
   184  type deadcodepass struct {
   185  	ctxt            *Link
   186  	markQueue       []*LSym            // symbols to flood fill in next pass
   187  	ifaceMethod     map[methodsig]bool // methods declared in reached interfaces
   188  	markableMethods []methodref        // methods of reached types
   189  	reflectMethod   bool
   190  }
   191  
   192  func (d *deadcodepass) cleanupReloc(r *Reloc) {
   193  	if r.Sym.Attr.Reachable() {
   194  		r.Type = obj.R_ADDR
   195  	} else {
   196  		if Debug['v'] > 1 {
   197  			fmt.Fprintf(d.ctxt.Bso, "removing method %s\n", r.Sym.Name)
   198  		}
   199  		r.Sym = nil
   200  		r.Siz = 0
   201  	}
   202  }
   203  
   204  // mark appends a symbol to the mark queue for flood filling.
   205  func (d *deadcodepass) mark(s, parent *LSym) {
   206  	if s == nil || s.Attr.Reachable() {
   207  		return
   208  	}
   209  	if s.Attr.ReflectMethod() {
   210  		d.reflectMethod = true
   211  	}
   212  	s.Attr |= AttrReachable
   213  	s.Reachparent = parent
   214  	d.markQueue = append(d.markQueue, s)
   215  }
   216  
   217  // markMethod marks a method as reachable.
   218  func (d *deadcodepass) markMethod(m methodref) {
   219  	for _, r := range m.r {
   220  		d.mark(r.Sym, m.src)
   221  		r.Type = obj.R_ADDR
   222  	}
   223  }
   224  
   225  // init marks all initial symbols as reachable.
   226  // In a typical binary, this is INITENTRY.
   227  func (d *deadcodepass) init() {
   228  	var names []string
   229  
   230  	if Thearch.Thechar == '5' {
   231  		// mark some functions that are only referenced after linker code editing
   232  		if d.ctxt.Goarm == 5 {
   233  			names = append(names, "_sfloat")
   234  		}
   235  		names = append(names, "runtime.read_tls_fallback")
   236  	}
   237  
   238  	if Buildmode == BuildmodeShared {
   239  		// Mark all symbols defined in this library as reachable when
   240  		// building a shared library.
   241  		for _, s := range d.ctxt.Allsym {
   242  			if s.Type != 0 && s.Type != obj.SDYNIMPORT {
   243  				d.mark(s, nil)
   244  			}
   245  		}
   246  	} else {
   247  		// In a normal binary, start at main.main and the init
   248  		// functions and mark what is reachable from there.
   249  		names = append(names, INITENTRY)
   250  		if Linkshared && Buildmode == BuildmodeExe {
   251  			names = append(names, "main.main", "main.init")
   252  		}
   253  		for _, name := range markextra {
   254  			names = append(names, name)
   255  		}
   256  		for _, s := range dynexp {
   257  			d.mark(s, nil)
   258  		}
   259  	}
   260  
   261  	for _, name := range names {
   262  		d.mark(Linkrlookup(d.ctxt, name, 0), nil)
   263  	}
   264  }
   265  
   266  // flood flood fills symbols reachable from the markQueue symbols.
   267  // As it goes, it collects methodref and interface method declarations.
   268  func (d *deadcodepass) flood() {
   269  	for len(d.markQueue) > 0 {
   270  		s := d.markQueue[0]
   271  		d.markQueue = d.markQueue[1:]
   272  		if s.Type == obj.STEXT {
   273  			if Debug['v'] > 1 {
   274  				fmt.Fprintf(d.ctxt.Bso, "marktext %s\n", s.Name)
   275  			}
   276  			for _, a := range s.Autom {
   277  				d.mark(a.Gotype, s)
   278  			}
   279  		}
   280  
   281  		if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' {
   282  			if decodetype_kind(s)&kindMask == kindInterface {
   283  				for _, sig := range decodetype_ifacemethods(s) {
   284  					if Debug['v'] > 1 {
   285  						fmt.Fprintf(d.ctxt.Bso, "reached iface method: %s\n", sig)
   286  					}
   287  					d.ifaceMethod[sig] = true
   288  				}
   289  			}
   290  		}
   291  
   292  		mpos := 0 // 0-3, the R_METHOD relocs of runtime.uncommontype
   293  		var methods []methodref
   294  		for i := 0; i < len(s.R); i++ {
   295  			r := &s.R[i]
   296  			if r.Sym == nil {
   297  				continue
   298  			}
   299  			if r.Type != obj.R_METHOD {
   300  				d.mark(r.Sym, s)
   301  				continue
   302  			}
   303  			// Collect rtype pointers to methods for
   304  			// later processing in deadcode.
   305  			if mpos == 0 {
   306  				m := methodref{src: s}
   307  				m.r[0] = r
   308  				methods = append(methods, m)
   309  			} else {
   310  				methods[len(methods)-1].r[mpos] = r
   311  			}
   312  			mpos++
   313  			if mpos == len(methodref{}.r) {
   314  				mpos = 0
   315  			}
   316  		}
   317  		if len(methods) > 0 {
   318  			// Decode runtime type information for type methods
   319  			// to help work out which methods can be called
   320  			// dynamically via interfaces.
   321  			methodsigs := decodetype_methods(s)
   322  			if len(methods) != len(methodsigs) {
   323  				panic(fmt.Sprintf("%q has %d method relocations for %d methods", s.Name, len(methods), len(methodsigs)))
   324  			}
   325  			for i, m := range methodsigs {
   326  				name := string(m)
   327  				name = name[:strings.Index(name, "(")]
   328  				if !strings.HasSuffix(methods[i].ifn().Name, name) {
   329  					panic(fmt.Sprintf("%q relocation for %q does not match method %q", s.Name, methods[i].ifn().Name, name))
   330  				}
   331  				methods[i].m = m
   332  			}
   333  			d.markableMethods = append(d.markableMethods, methods...)
   334  		}
   335  
   336  		if s.Pcln != nil {
   337  			for i := range s.Pcln.Funcdata {
   338  				d.mark(s.Pcln.Funcdata[i], s)
   339  			}
   340  		}
   341  		d.mark(s.Gotype, s)
   342  		d.mark(s.Sub, s)
   343  		d.mark(s.Outer, s)
   344  	}
   345  }