|
|
|
package ir
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"go/types"
|
|
|
|
|
|
|
|
"golang.org/x/tools/go/ssa"
|
|
|
|
)
|
|
|
|
|
|
|
|
// This file implements several optimization passes (analysis + transform) to
|
|
|
|
// optimize code in SSA form before it is compiled to LLVM IR. It is based on
|
|
|
|
// the IR defined in ir.go.
|
|
|
|
|
|
|
|
// Make a readable version of a method signature (including the function name,
|
|
|
|
// excluding the receiver name). This string is used internally to match
|
|
|
|
// interfaces and to call the correct method on an interface. Examples:
|
|
|
|
//
|
|
|
|
// String() string
|
|
|
|
// Read([]byte) (int, error)
|
|
|
|
func MethodSignature(method *types.Func) string {
|
|
|
|
return method.Name() + signature(method.Type().(*types.Signature))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a readable version of a function (pointer) signature.
|
|
|
|
// Examples:
|
|
|
|
//
|
|
|
|
// () string
|
|
|
|
// (string, int) (int, error)
|
|
|
|
func signature(sig *types.Signature) string {
|
|
|
|
s := ""
|
|
|
|
if sig.Params().Len() == 0 {
|
|
|
|
s += "()"
|
|
|
|
} else {
|
|
|
|
s += "("
|
|
|
|
for i := 0; i < sig.Params().Len(); i++ {
|
|
|
|
if i > 0 {
|
|
|
|
s += ", "
|
|
|
|
}
|
|
|
|
s += sig.Params().At(i).Type().String()
|
|
|
|
}
|
|
|
|
s += ")"
|
|
|
|
}
|
|
|
|
if sig.Results().Len() == 0 {
|
|
|
|
// keep as-is
|
|
|
|
} else if sig.Results().Len() == 1 {
|
|
|
|
s += " " + sig.Results().At(0).Type().String()
|
|
|
|
} else {
|
|
|
|
s += " ("
|
|
|
|
for i := 0; i < sig.Results().Len(); i++ {
|
|
|
|
if i > 0 {
|
|
|
|
s += ", "
|
|
|
|
}
|
|
|
|
s += sig.Results().At(i).Type().String()
|
|
|
|
}
|
|
|
|
s += ")"
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simple pass that removes dead code. This pass makes later analysis passes
|
|
|
|
// more useful.
|
|
|
|
func (p *Program) SimpleDCE() error {
|
|
|
|
// Unmark all functions.
|
|
|
|
for _, f := range p.Functions {
|
|
|
|
f.flag = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initial set of live functions. Include main.main, *.init and runtime.*
|
|
|
|
// functions.
|
|
|
|
main, ok := p.mainPkg.Members["main"].(*ssa.Function)
|
|
|
|
if !ok {
|
|
|
|
if p.mainPkg.Members["main"] == nil {
|
|
|
|
return errors.New("function main is undeclared in the main package")
|
|
|
|
} else {
|
|
|
|
return errors.New("cannot declare main - must be func")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
runtimePkg := p.Program.ImportedPackage("runtime")
|
|
|
|
mathPkg := p.Program.ImportedPackage("math")
|
|
|
|
taskPkg := p.Program.ImportedPackage("internal/task")
|
|
|
|
p.GetFunction(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(p.Program, instr.X.Type()) {
|
|
|
|
fn := p.Program.MethodValue(sel)
|
|
|
|
callee := p.GetFunction(fn)
|
|
|
|
if callee == nil {
|
|
|
|
// TODO: why is this necessary?
|
|
|
|
p.addFunction(fn)
|
|
|
|
callee = p.GetFunction(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.GetFunction(operand)
|
|
|
|
if f == nil {
|
|
|
|
// FIXME HACK: this function should have been
|
|
|
|
// discovered already. It is not for bound methods.
|
|
|
|
p.addFunction(operand)
|
|
|
|
f = p.GetFunction(operand)
|
|
|
|
}
|
|
|
|
if !f.flag {
|
|
|
|
f.flag = true
|
|
|
|
worklist = append(worklist, operand)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove unmarked functions.
|
|
|
|
livefunctions := []*Function{}
|
|
|
|
for _, f := range p.Functions {
|
|
|
|
if f.flag {
|
|
|
|
livefunctions = append(livefunctions, f)
|
|
|
|
} else {
|
|
|
|
delete(p.functionMap, f.Function)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p.Functions = livefunctions
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|