tinygo/ir.go
Ayke van Laethem 574c7ec047
Refactor: Use our own types as much as possible from the analysis
Previously, mostly the types from the ssa package were used directly
with the types from analysis.go as an overlay. This commit uses these
types everywhere and renames a few things here and there to make things
clearer.
2018-08-17 23:13:32 +02:00

156 строки
3,7 КиБ
Go

package main
import (
"go/types"
"sort"
"strings"
"github.com/aykevl/llvm/bindings/go/llvm"
"golang.org/x/tools/go/ssa"
)
// View on all functions, types, and globals in a program, with analysis
// results.
type Program struct {
Functions []*Function
functionMap map[*ssa.Function]*Function
Globals []*Global
globalMap map[*ssa.Global]*Global
NamedTypes []*NamedType
needsScheduler bool
goCalls []*ssa.Go
typesWithMethods map[string]*InterfaceType
typesWithoutMethods map[string]int
methodSignatureNames map[string]int
}
// Function or method.
type Function struct {
fn *ssa.Function
llvmFn llvm.Value
blocking bool
parents []*Function // calculated by AnalyseCallgraph
children []*Function
}
// Global variable, possibly constant.
type Global struct {
g *ssa.Global
llvmGlobal llvm.Value
}
// Type with a name and possibly methods.
type NamedType struct {
t *ssa.Type
}
// Type that is at some point put in an interface.
type InterfaceType struct {
t types.Type
Num int
Methods map[string]*types.Selection
}
func NewProgram() *Program {
return &Program{
functionMap: make(map[*ssa.Function]*Function),
globalMap: make(map[*ssa.Global]*Global),
methodSignatureNames: make(map[string]int),
}
}
// Add a package to this Program. All packages need to be added first before any
// analysis is done for correct results.
func (p *Program) AddPackage(pkg *ssa.Package) {
memberNames := make([]string, 0)
for name := range pkg.Members {
if isCGoInternal(name) {
continue
}
memberNames = append(memberNames, name)
}
sort.Strings(memberNames)
for _, name := range memberNames {
member := pkg.Members[name]
switch member := member.(type) {
case *ssa.Function:
if isCGoInternal(member.Name()) {
continue
}
p.addFunction(member)
case *ssa.Type:
t := &NamedType{t: member}
p.NamedTypes = append(p.NamedTypes, t)
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:
g := &Global{g: member}
p.Globals = append(p.Globals, g)
p.globalMap[member] = g
}
}
}
func (p *Program) addFunction(ssaFn *ssa.Function) {
f := &Function{fn: ssaFn}
p.Functions = append(p.Functions, f)
p.functionMap[ssaFn] = f
}
func (p *Program) GetFunction(ssaFn *ssa.Function) *Function {
return p.functionMap[ssaFn]
}
func (p *Program) GetGlobal(ssaGlobal *ssa.Global) *Global {
return p.globalMap[ssaGlobal]
}
// Return the link name for this function.
func (f *Function) Name(blocking bool) string {
suffix := ""
if blocking {
suffix = "$async"
}
if f.fn.Signature.Recv() != nil {
// Method on a defined type (which may be a pointer).
return f.fn.RelString(nil) + suffix
} else {
// Bare function.
if name := f.CName(); name != "" {
// Name CGo functions directly.
return name
} else {
name := f.fn.RelString(nil) + suffix
if f.fn.Pkg.Pkg.Path() == "runtime" && strings.HasPrefix(f.fn.Name(), "_llvm_") {
// Special case for LLVM intrinsics in the runtime.
name = "llvm." + strings.Replace(f.fn.Name()[len("_llvm_"):], "_", ".", -1)
}
return name
}
}
}
// Return the name of the C function if this is a CGo wrapper. Otherwise, return
// a zero-length string.
func (f *Function) CName() string {
name := f.fn.Name()
if strings.HasPrefix(name, "_Cfunc_") {
return name[len("_Cfunc_"):]
}
return ""
}
// Return the link name for this global.
func (g *Global) Name() string {
if strings.HasPrefix(g.g.Name(), "_extern_") {
return g.g.Name()[len("_extern_"):]
} else {
return g.g.RelString(nil)
}
}