| // Copyright 2014 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. |
| |
| // Removal of dead code and data. |
| |
| package main |
| |
| import "cmd/internal/goobj" |
| |
| // dead removes unreachable code and data from the program. |
| // It is basically a mark-sweep garbage collection: traverse all the |
| // symbols reachable from the entry (startSymID) and then delete |
| // the rest. |
| func (p *Prog) dead() { |
| p.Dead = make(map[goobj.SymID]bool) |
| reachable := make(map[goobj.SymID]bool) |
| p.walkDead(p.startSym, reachable) |
| |
| for sym := range p.Syms { |
| if !reachable[sym] { |
| delete(p.Syms, sym) |
| p.Dead[sym] = true |
| } |
| } |
| |
| for sym := range p.Missing { |
| if !reachable[sym] { |
| delete(p.Missing, sym) |
| p.Dead[sym] = true |
| } |
| } |
| |
| p.SymOrder = removeDead(p.SymOrder, reachable) |
| |
| for _, pkg := range p.Packages { |
| pkg.Syms = removeDead(pkg.Syms, reachable) |
| } |
| } |
| |
| // walkDead traverses the symbols reachable from sym, adding them to reachable. |
| // The caller has verified that reachable[sym] = false. |
| func (p *Prog) walkDead(sym goobj.SymID, reachable map[goobj.SymID]bool) { |
| reachable[sym] = true |
| s := p.Syms[sym] |
| if s == nil { |
| return |
| } |
| for i := range s.Reloc { |
| r := &s.Reloc[i] |
| if !reachable[r.Sym] { |
| p.walkDead(r.Sym, reachable) |
| } |
| } |
| if s.Func != nil { |
| for _, fdata := range s.Func.FuncData { |
| if fdata.Sym.Name != "" && !reachable[fdata.Sym] { |
| p.walkDead(fdata.Sym, reachable) |
| } |
| } |
| } |
| } |
| |
| // removeDead removes unreachable (dead) symbols from syms, |
| // returning a shortened slice using the same underlying array. |
| func removeDead(syms []*Sym, reachable map[goobj.SymID]bool) []*Sym { |
| keep := syms[:0] |
| for _, sym := range syms { |
| if reachable[sym.SymID] { |
| keep = append(keep, sym) |
| } |
| } |
| return keep |
| } |