Add dead code elimination (DCE) pass

This should make it much easier (in the future) to use the standard
library when unused functions contain unimplemented features. But more
importantly, it makes later passes better and makes compiling faster by
not having to scan dead code.
Этот коммит содержится в:
Ayke van Laethem 2018-08-19 20:44:55 +02:00
родитель fae4c7f555
коммит 7460e10894
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
3 изменённых файлов: 89 добавлений и 0 удалений

Просмотреть файл

@ -205,6 +205,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
for _, pkg := range packageList { for _, pkg := range packageList {
c.ir.AddPackage(pkg) c.ir.AddPackage(pkg)
} }
c.ir.SimpleDCE() // remove most dead code
c.ir.AnalyseCallgraph() // set up callgraph c.ir.AnalyseCallgraph() // set up callgraph
c.ir.AnalyseInterfaceConversions() // determine which types are converted to an interface c.ir.AnalyseInterfaceConversions() // determine which types are converted to an interface
c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively) c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively)

2
ir.go
Просмотреть файл

@ -31,6 +31,7 @@ type Function struct {
fn *ssa.Function fn *ssa.Function
llvmFn llvm.Value llvmFn llvm.Value
blocking bool blocking bool
flag bool // used by dead code elimination
parents []*Function // calculated by AnalyseCallgraph parents []*Function // calculated by AnalyseCallgraph
children []*Function children []*Function
} }
@ -39,6 +40,7 @@ type Function struct {
type Global struct { type Global struct {
g *ssa.Global g *ssa.Global
llvmGlobal llvm.Value llvmGlobal llvm.Value
flag bool // used by dead code elimination
} }
// Type with a name and possibly methods. // Type with a name and possibly methods.

Просмотреть файл

@ -180,6 +180,92 @@ func (p *Program) AnalyseGoCalls() {
} }
} }
// Simple pass that removes dead code. This pass makes later analysis passes
// more useful.
func (p *Program) SimpleDCE() {
// Unmark all functions and globals.
for _, f := range p.Functions {
f.flag = false
}
for _, f := range p.Globals {
f.flag = false
}
// Initial set of live functions. Include main.main, *.init and runtime.*
// functions.
main := p.mainPkg.Members["main"].(*ssa.Function)
runtimePkg := p.program.ImportedPackage("runtime")
p.GetFunction(main).flag = true
worklist := []*ssa.Function{main}
for _, f := range p.Functions {
if f.fn.Synthetic == "package initializer" || f.fn.Pkg == runtimePkg {
if f.flag || isCGoInternal(f.fn.Name()) {
continue
}
f.flag = true
worklist = append(worklist, f.fn)
}
}
// Mark all called functions recursively.
for len(worklist) != 0 {
f := worklist[len(worklist)-1]
worklist = worklist[:len(worklist)-1]
for _, block := range f.Blocks {
for _, instr := range block.Instrs {
if instr, ok := instr.(*ssa.MakeInterface); ok {
for _, sel := range getAllMethods(p.program, instr.X.Type()) {
callee := p.GetFunction(p.program.MethodValue(sel))
if !callee.flag {
callee.flag = true
worklist = append(worklist, callee.fn)
}
}
}
for _, operand := range instr.Operands(nil) {
if operand == nil || *operand == nil || isCGoInternal((*operand).Name()) {
continue
}
switch operand := (*operand).(type) {
case *ssa.Function:
f := p.GetFunction(operand)
if !f.flag {
f.flag = true
worklist = append(worklist, operand)
}
case *ssa.Global:
// TODO: globals that reference other globals
global := p.GetGlobal(operand)
global.flag = true
}
}
}
}
}
// Remove unmarked functions.
livefunctions := []*Function{p.GetFunction(main)}
for _, f := range p.Functions {
if f.flag {
livefunctions = append(livefunctions, f)
} else {
delete(p.functionMap, f.fn)
}
}
p.Functions = livefunctions
// Remove unmarked globals.
liveglobals := []*Global{}
for _, g := range p.Globals {
if g.flag {
liveglobals = append(liveglobals, g)
} else {
delete(p.globalMap, g.g)
}
}
p.Globals = liveglobals
}
// Whether this function needs a scheduler. // Whether this function needs a scheduler.
// //
// Depends on AnalyseGoCalls. // Depends on AnalyseGoCalls.