
This package was long making the design of the compiler more complicated than it needs to be. Previously this package implemented several optimization passes, but those passes have since moved to work directly with LLVM IR instead of Go SSA. The only remaining pass is the SimpleDCE pass. This commit removes the *ir.Function type that permeated the whole compiler and instead switches to use *ssa.Function directly. The SimpleDCE pass is kept but is far less tightly coupled to the rest of the compiler so that it can easily be removed once the switch to building and caching packages individually happens.
164 строки
4,3 КиБ
Go
164 строки
4,3 КиБ
Go
package compiler
|
|
|
|
// This file implements a simple reachability analysis, to reduce compile time.
|
|
// This DCE pass used to be necessary for improving other passes but now it
|
|
// isn't necessary anymore.
|
|
|
|
import (
|
|
"errors"
|
|
"go/types"
|
|
"sort"
|
|
|
|
"github.com/tinygo-org/tinygo/loader"
|
|
"golang.org/x/tools/go/ssa"
|
|
)
|
|
|
|
type dceState struct {
|
|
*compilerContext
|
|
functions []*dceFunction
|
|
functionMap map[*ssa.Function]*dceFunction
|
|
}
|
|
|
|
type dceFunction struct {
|
|
*ssa.Function
|
|
functionInfo
|
|
flag bool // used by dead code elimination
|
|
}
|
|
|
|
func (p *dceState) addFunction(ssaFn *ssa.Function) {
|
|
if _, ok := p.functionMap[ssaFn]; ok {
|
|
return
|
|
}
|
|
f := &dceFunction{Function: ssaFn}
|
|
f.functionInfo = p.getFunctionInfo(ssaFn)
|
|
p.functions = append(p.functions, f)
|
|
p.functionMap[ssaFn] = f
|
|
|
|
for _, anon := range ssaFn.AnonFuncs {
|
|
p.addFunction(anon)
|
|
}
|
|
}
|
|
|
|
// simpleDCE returns a list of alive functions in the program. Compiling only
|
|
// these functions makes the compiler faster.
|
|
//
|
|
// This functionality will likely be replaced in the future with build caching.
|
|
func (c *compilerContext) simpleDCE(lprogram *loader.Program) ([]*ssa.Function, error) {
|
|
mainPkg := c.program.Package(lprogram.MainPkg().Pkg)
|
|
if mainPkg == nil {
|
|
panic("could not find main package")
|
|
}
|
|
p := &dceState{
|
|
compilerContext: c,
|
|
functionMap: make(map[*ssa.Function]*dceFunction),
|
|
}
|
|
|
|
for _, pkg := range lprogram.Sorted() {
|
|
pkg := c.program.Package(pkg.Pkg)
|
|
memberNames := make([]string, 0)
|
|
for name := range pkg.Members {
|
|
memberNames = append(memberNames, name)
|
|
}
|
|
sort.Strings(memberNames)
|
|
|
|
for _, name := range memberNames {
|
|
member := pkg.Members[name]
|
|
switch member := member.(type) {
|
|
case *ssa.Function:
|
|
p.addFunction(member)
|
|
case *ssa.Type:
|
|
methods := getAllMethods(pkg.Prog, member.Type())
|
|
if !types.IsInterface(member.Type()) {
|
|
// named type
|
|
for _, method := range methods {
|
|
p.addFunction(pkg.Prog.MethodValue(method))
|
|
}
|
|
}
|
|
case *ssa.Global:
|
|
// Ignore. Globals are not handled here.
|
|
case *ssa.NamedConst:
|
|
// Ignore: these are already resolved.
|
|
default:
|
|
panic("unknown member type: " + member.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initial set of live functions. Include main.main, *.init and runtime.*
|
|
// functions.
|
|
main, ok := mainPkg.Members["main"].(*ssa.Function)
|
|
if !ok {
|
|
if mainPkg.Members["main"] == nil {
|
|
return nil, errors.New("function main is undeclared in the main package")
|
|
} else {
|
|
return nil, errors.New("cannot declare main - must be func")
|
|
}
|
|
}
|
|
runtimePkg := c.program.ImportedPackage("runtime")
|
|
mathPkg := c.program.ImportedPackage("math")
|
|
taskPkg := c.program.ImportedPackage("internal/task")
|
|
p.functionMap[main].flag = true
|
|
worklist := []*ssa.Function{main}
|
|
for _, f := range p.functions {
|
|
if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || f.Pkg == taskPkg || (f.Pkg == mathPkg && f.Pkg != nil) {
|
|
if f.flag {
|
|
continue
|
|
}
|
|
f.flag = true
|
|
worklist = append(worklist, f.Function)
|
|
}
|
|
}
|
|
|
|
// 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(c.program, instr.X.Type()) {
|
|
fn := c.program.MethodValue(sel)
|
|
callee := p.functionMap[fn]
|
|
if callee == nil {
|
|
// TODO: why is this necessary?
|
|
p.addFunction(fn)
|
|
callee = p.functionMap[fn]
|
|
}
|
|
if !callee.flag {
|
|
callee.flag = true
|
|
worklist = append(worklist, callee.Function)
|
|
}
|
|
}
|
|
}
|
|
for _, operand := range instr.Operands(nil) {
|
|
if operand == nil || *operand == nil {
|
|
continue
|
|
}
|
|
switch operand := (*operand).(type) {
|
|
case *ssa.Function:
|
|
f := p.functionMap[operand]
|
|
if f == nil {
|
|
// FIXME HACK: this function should have been
|
|
// discovered already. It is not for bound methods.
|
|
p.addFunction(operand)
|
|
f = p.functionMap[operand]
|
|
}
|
|
if !f.flag {
|
|
f.flag = true
|
|
worklist = append(worklist, operand)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return all live functions.
|
|
liveFunctions := []*ssa.Function{}
|
|
for _, f := range p.functions {
|
|
if f.flag {
|
|
liveFunctions = append(liveFunctions, f.Function)
|
|
}
|
|
}
|
|
return liveFunctions, nil
|
|
}
|