compiler: refactor IR parts into separate package
Этот коммит содержится в:
родитель
473e71b573
коммит
b75a02e66d
5 изменённых файлов: 182 добавлений и 169 удалений
2
Makefile
2
Makefile
|
@ -54,7 +54,7 @@ clean:
|
|||
@rm -rf build
|
||||
|
||||
fmt:
|
||||
@go fmt . ./src/examples/* ./src/machine ./src/runtime ./src/sync
|
||||
@go fmt . ./ir ./src/examples/* ./src/machine ./src/runtime ./src/sync
|
||||
|
||||
gen-device: gen-device-nrf gen-device-avr
|
||||
|
||||
|
|
229
compiler.go
229
compiler.go
|
@ -14,6 +14,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/aykevl/llvm/bindings/go/llvm"
|
||||
"github.com/aykevl/tinygo/ir"
|
||||
"go/parser"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
|
@ -53,12 +54,12 @@ type Compiler struct {
|
|||
coroEndFunc llvm.Value
|
||||
coroFreeFunc llvm.Value
|
||||
initFuncs []llvm.Value
|
||||
deferFuncs []*Function
|
||||
ir *Program
|
||||
deferFuncs []*ir.Function
|
||||
ir *ir.Program
|
||||
}
|
||||
|
||||
type Frame struct {
|
||||
fn *Function
|
||||
fn *ir.Function
|
||||
params map[*ssa.Parameter]int // arguments to the function
|
||||
locals map[ssa.Value]llvm.Value // local variables
|
||||
blocks map[*ssa.BasicBlock]llvm.BasicBlock
|
||||
|
@ -77,8 +78,6 @@ type Phi struct {
|
|||
llvm llvm.Value
|
||||
}
|
||||
|
||||
var cgoWrapperError = errors.New("tinygo internal: cgo wrapper")
|
||||
|
||||
func NewCompiler(pkgName, triple string, dumpSSA bool) (*Compiler, error) {
|
||||
c := &Compiler{
|
||||
dumpSSA: dumpSSA,
|
||||
|
@ -178,7 +177,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
}
|
||||
}
|
||||
|
||||
c.ir = NewProgram(lprogram, mainPath)
|
||||
c.ir = ir.NewProgram(lprogram, mainPath)
|
||||
|
||||
// Make a list of packages in import order.
|
||||
packageList := []*ssa.Package{}
|
||||
|
@ -186,7 +185,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
worklist := []string{"runtime", mainPath}
|
||||
for len(worklist) != 0 {
|
||||
pkgPath := worklist[0]
|
||||
pkg := c.ir.program.ImportedPackage(pkgPath)
|
||||
pkg := c.ir.Program.ImportedPackage(pkgPath)
|
||||
if pkg == nil {
|
||||
// Non-SSA package (e.g. cgo).
|
||||
packageSet[pkgPath] = struct{}{}
|
||||
|
@ -244,22 +243,22 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
|
||||
// Declare all named struct types.
|
||||
for _, t := range c.ir.NamedTypes {
|
||||
if named, ok := t.t.Type().(*types.Named); ok {
|
||||
if named, ok := t.Type.Type().(*types.Named); ok {
|
||||
if _, ok := named.Underlying().(*types.Struct); ok {
|
||||
t.llvmType = c.ctx.StructCreateNamed(named.Obj().Pkg().Path() + "." + named.Obj().Name())
|
||||
t.LLVMType = c.ctx.StructCreateNamed(named.Obj().Pkg().Path() + "." + named.Obj().Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define all named struct types.
|
||||
for _, t := range c.ir.NamedTypes {
|
||||
if named, ok := t.t.Type().(*types.Named); ok {
|
||||
if named, ok := t.Type.Type().(*types.Named); ok {
|
||||
if st, ok := named.Underlying().(*types.Struct); ok {
|
||||
llvmType, err := c.getLLVMType(st)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.llvmType.StructSetBody(llvmType.StructElementTypes(), false)
|
||||
t.LLVMType.StructSetBody(llvmType.StructElementTypes(), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,7 +266,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
// Declare all globals. These will get an initializer when parsing "package
|
||||
// initializer" functions.
|
||||
for _, g := range c.ir.Globals {
|
||||
typ := g.g.Type().(*types.Pointer).Elem()
|
||||
typ := g.Type().(*types.Pointer).Elem()
|
||||
llvmType, err := c.getLLVMType(typ)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -276,7 +275,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
if global.IsNil() {
|
||||
global = llvm.AddGlobal(c.mod, llvmType, g.LinkName())
|
||||
}
|
||||
g.llvmGlobal = global
|
||||
g.LLVMGlobal = global
|
||||
if !g.IsExtern() {
|
||||
global.SetLinkage(llvm.InternalLinkage)
|
||||
initializer, err := getZeroValue(llvmType)
|
||||
|
@ -298,9 +297,9 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
|
||||
// Find and interpret package initializers.
|
||||
for _, frame := range frames {
|
||||
if frame.fn.fn.Synthetic == "package initializer" {
|
||||
c.initFuncs = append(c.initFuncs, frame.fn.llvmFn)
|
||||
if len(frame.fn.fn.Blocks) != 1 {
|
||||
if frame.fn.Synthetic == "package initializer" {
|
||||
c.initFuncs = append(c.initFuncs, frame.fn.LLVMFn)
|
||||
if len(frame.fn.Blocks) != 1 {
|
||||
panic("unexpected number of basic blocks in package initializer")
|
||||
}
|
||||
// Try to interpret as much as possible of the init() function.
|
||||
|
@ -309,7 +308,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
// continues at runtime).
|
||||
// This should only happen when it hits a function call or the end
|
||||
// of the block, ideally.
|
||||
err := c.ir.Interpret(frame.fn.fn.Blocks[0], c.dumpSSA)
|
||||
err := c.ir.Interpret(frame.fn.Blocks[0], c.dumpSSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -322,7 +321,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
|
||||
// Set values for globals (after package initializer has been interpreted).
|
||||
for _, g := range c.ir.Globals {
|
||||
if g.initializer == nil {
|
||||
if g.Initializer() == nil {
|
||||
continue
|
||||
}
|
||||
err := c.parseGlobalInitializer(g)
|
||||
|
@ -336,11 +335,11 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
if frame.fn.CName() != "" {
|
||||
continue
|
||||
}
|
||||
if frame.fn.fn.Blocks == nil {
|
||||
if frame.fn.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
var err error
|
||||
if frame.fn.fn.Synthetic == "package initializer" {
|
||||
if frame.fn.Synthetic == "package initializer" {
|
||||
continue // already done
|
||||
} else {
|
||||
err = c.parseFunc(frame)
|
||||
|
@ -364,7 +363,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
|
||||
// Get the real param type and cast to it.
|
||||
valueTypes := []llvm.Type{llvmFn.Type(), llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)}
|
||||
for _, param := range fn.fn.Params {
|
||||
for _, param := range fn.Params {
|
||||
llvmType, err := c.getLLVMType(param.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -377,14 +376,14 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
// Extract the params from the struct.
|
||||
forwardParams := []llvm.Value{}
|
||||
zero := llvm.ConstInt(llvm.Int32Type(), 0, false)
|
||||
for i := range fn.fn.Params {
|
||||
for i := range fn.Params {
|
||||
gep := c.builder.CreateGEP(contextPtr, []llvm.Value{zero, llvm.ConstInt(llvm.Int32Type(), uint64(i+2), false)}, "gep")
|
||||
forwardParam := c.builder.CreateLoad(gep, "param")
|
||||
forwardParams = append(forwardParams, forwardParam)
|
||||
}
|
||||
|
||||
// Call real function (of which this is a wrapper).
|
||||
c.builder.CreateCall(fn.llvmFn, forwardParams, "")
|
||||
c.builder.CreateCall(fn.LLVMFn, forwardParams, "")
|
||||
c.builder.CreateRetVoid()
|
||||
}
|
||||
|
||||
|
@ -400,7 +399,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
c.builder.CreateRetVoid()
|
||||
|
||||
// Adjust main function.
|
||||
realMain := c.mod.NamedFunction(c.ir.mainPkg.Pkg.Path() + ".main")
|
||||
realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
|
||||
if c.ir.NeedsScheduler() {
|
||||
c.mod.NamedFunction("runtime.main_mainAsync").ReplaceAllUsesWith(realMain)
|
||||
} else {
|
||||
|
@ -441,11 +440,11 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
|
|||
}
|
||||
c.ir.SortMethods(methods)
|
||||
for _, method := range methods {
|
||||
f := c.ir.GetFunction(c.ir.program.MethodValue(method))
|
||||
if f.llvmFn.IsNil() {
|
||||
f := c.ir.GetFunction(c.ir.Program.MethodValue(method))
|
||||
if f.LLVMFn.IsNil() {
|
||||
return errors.New("cannot find function: " + f.LinkName())
|
||||
}
|
||||
fn := llvm.ConstBitCast(f.llvmFn, c.i8ptrType)
|
||||
fn := llvm.ConstBitCast(f.LLVMFn, c.i8ptrType)
|
||||
funcPointers = append(funcPointers, fn)
|
||||
signatureNum := c.ir.MethodNum(method.Obj().(*types.Func))
|
||||
signature := llvm.ConstInt(llvm.Int16Type(), uint64(signatureNum), false)
|
||||
|
@ -780,29 +779,7 @@ func (c *Compiler) getDIType(typ types.Type) (llvm.Metadata, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Get all methods of a type.
|
||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
ms := prog.MethodSets.MethodSet(typ)
|
||||
methods := make([]*types.Selection, ms.Len())
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
methods[i] = ms.At(i)
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
// Return true if this is a CGo-internal function that can be ignored.
|
||||
func isCGoInternal(name string) bool {
|
||||
if strings.HasPrefix(name, "_Cgo_") || strings.HasPrefix(name, "_cgo") {
|
||||
// _Cgo_ptr, _Cgo_use, _cgoCheckResult, _cgo_runtime_cgocall
|
||||
return true // CGo-internal functions
|
||||
}
|
||||
if strings.HasPrefix(name, "__cgofn__cgo_") {
|
||||
return true // CGo function pointer in global scope
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
|
||||
func (c *Compiler) parseFuncDecl(f *ir.Function) (*Frame, error) {
|
||||
frame := &Frame{
|
||||
fn: f,
|
||||
params: make(map[*ssa.Parameter]int),
|
||||
|
@ -813,22 +790,22 @@ func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
|
|||
|
||||
var retType llvm.Type
|
||||
if frame.blocking {
|
||||
if f.fn.Signature.Results() != nil {
|
||||
if f.Signature.Results() != nil {
|
||||
return nil, errors.New("todo: return values in blocking function")
|
||||
}
|
||||
retType = c.i8ptrType
|
||||
} else if f.fn.Signature.Results() == nil {
|
||||
} else if f.Signature.Results() == nil {
|
||||
retType = llvm.VoidType()
|
||||
} else if f.fn.Signature.Results().Len() == 1 {
|
||||
} else if f.Signature.Results().Len() == 1 {
|
||||
var err error
|
||||
retType, err = c.getLLVMType(f.fn.Signature.Results().At(0).Type())
|
||||
retType, err = c.getLLVMType(f.Signature.Results().At(0).Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
results := make([]llvm.Type, 0, f.fn.Signature.Results().Len())
|
||||
for i := 0; i < f.fn.Signature.Results().Len(); i++ {
|
||||
typ, err := c.getLLVMType(f.fn.Signature.Results().At(i).Type())
|
||||
results := make([]llvm.Type, 0, f.Signature.Results().Len())
|
||||
for i := 0; i < f.Signature.Results().Len(); i++ {
|
||||
typ, err := c.getLLVMType(f.Signature.Results().At(i).Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -841,7 +818,7 @@ func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
|
|||
if frame.blocking {
|
||||
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
|
||||
}
|
||||
for i, param := range f.fn.Params {
|
||||
for i, param := range f.Params {
|
||||
paramType, err := c.getLLVMType(param.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -859,22 +836,22 @@ func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
|
|||
fnType := llvm.FunctionType(retType, paramTypes, false)
|
||||
|
||||
name := f.LinkName()
|
||||
frame.fn.llvmFn = c.mod.NamedFunction(name)
|
||||
if frame.fn.llvmFn.IsNil() {
|
||||
frame.fn.llvmFn = llvm.AddFunction(c.mod, name, fnType)
|
||||
frame.fn.LLVMFn = c.mod.NamedFunction(name)
|
||||
if frame.fn.LLVMFn.IsNil() {
|
||||
frame.fn.LLVMFn = llvm.AddFunction(c.mod, name, fnType)
|
||||
}
|
||||
|
||||
if c.debug && f.fn.Syntax() != nil && len(f.fn.Blocks) != 0 {
|
||||
if c.debug && f.Syntax() != nil && len(f.Blocks) != 0 {
|
||||
// Create debug info file if needed.
|
||||
pos := c.ir.program.Fset.Position(f.fn.Syntax().Pos())
|
||||
pos := c.ir.Program.Fset.Position(f.Syntax().Pos())
|
||||
if _, ok := c.difiles[pos.Filename]; !ok {
|
||||
dir, file := filepath.Split(pos.Filename)
|
||||
c.difiles[pos.Filename] = c.dibuilder.CreateFile(file, dir[:len(dir)-1])
|
||||
}
|
||||
|
||||
// Debug info for this function.
|
||||
diparams := make([]llvm.Metadata, 0, len(f.fn.Params))
|
||||
for _, param := range f.fn.Params {
|
||||
diparams := make([]llvm.Metadata, 0, len(f.Params))
|
||||
for _, param := range f.Params {
|
||||
ditype, err := c.getDIType(param.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -887,7 +864,7 @@ func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
|
|||
Flags: 0, // ?
|
||||
})
|
||||
frame.difunc = c.dibuilder.CreateFunction(c.difiles[pos.Filename], llvm.DIFunction{
|
||||
Name: f.fn.RelString(nil),
|
||||
Name: f.RelString(nil),
|
||||
LinkageName: f.LinkName(),
|
||||
File: c.difiles[pos.Filename],
|
||||
Line: pos.Line,
|
||||
|
@ -898,7 +875,7 @@ func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
|
|||
Flags: llvm.FlagPrototyped,
|
||||
Optimized: true,
|
||||
})
|
||||
frame.fn.llvmFn.SetSubprogram(frame.difunc)
|
||||
frame.fn.LLVMFn.SetSubprogram(frame.difunc)
|
||||
}
|
||||
|
||||
return frame, nil
|
||||
|
@ -932,24 +909,24 @@ func (c *Compiler) initMapNewBucket(prefix string, mapType *types.Map) (llvm.Val
|
|||
return bucket, keySize, valueSize, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) parseGlobalInitializer(g *Global) error {
|
||||
func (c *Compiler) parseGlobalInitializer(g *ir.Global) error {
|
||||
if g.IsExtern() {
|
||||
return nil
|
||||
}
|
||||
llvmValue, err := c.getInterpretedValue(g.LinkName(), g.initializer)
|
||||
llvmValue, err := c.getInterpretedValue(g.LinkName(), g.Initializer())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.llvmGlobal.SetInitializer(llvmValue)
|
||||
g.LLVMGlobal.SetInitializer(llvmValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Turn a computed Value type (ConstValue, ArrayValue, etc.) into a LLVM value.
|
||||
// This is used to set the initializer of globals after they have been
|
||||
// calculated by the package initializer interpreter.
|
||||
func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value, error) {
|
||||
func (c *Compiler) getInterpretedValue(prefix string, value ir.Value) (llvm.Value, error) {
|
||||
switch value := value.(type) {
|
||||
case *ArrayValue:
|
||||
case *ir.ArrayValue:
|
||||
vals := make([]llvm.Value, len(value.Elems))
|
||||
for i, elem := range value.Elems {
|
||||
val, err := c.getInterpretedValue(prefix+"$arrayval", elem)
|
||||
|
@ -964,10 +941,10 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
}
|
||||
return llvm.ConstArray(subTyp, vals), nil
|
||||
|
||||
case *ConstValue:
|
||||
case *ir.ConstValue:
|
||||
return c.parseConst(prefix, value.Expr)
|
||||
|
||||
case *FunctionValue:
|
||||
case *ir.FunctionValue:
|
||||
if value.Elem == nil {
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
|
@ -976,19 +953,19 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
return getZeroValue(llvmType)
|
||||
}
|
||||
fn := c.ir.GetFunction(value.Elem)
|
||||
ptr := fn.llvmFn
|
||||
if c.ir.SignatureNeedsContext(fn.fn.Signature) {
|
||||
ptr := fn.LLVMFn
|
||||
if c.ir.SignatureNeedsContext(fn.Signature) {
|
||||
// Create closure value: {context, function pointer}
|
||||
ptr = llvm.ConstStruct([]llvm.Value{llvm.ConstPointerNull(c.i8ptrType), ptr}, false)
|
||||
}
|
||||
return ptr, nil
|
||||
|
||||
case *GlobalValue:
|
||||
case *ir.GlobalValue:
|
||||
zero := llvm.ConstInt(llvm.Int32Type(), 0, false)
|
||||
ptr := llvm.ConstInBoundsGEP(value.Global.llvmGlobal, []llvm.Value{zero})
|
||||
ptr := llvm.ConstInBoundsGEP(value.Global.LLVMGlobal, []llvm.Value{zero})
|
||||
return ptr, nil
|
||||
|
||||
case *InterfaceValue:
|
||||
case *ir.InterfaceValue:
|
||||
underlying := llvm.ConstPointerNull(c.i8ptrType) // could be any 0 value
|
||||
if value.Elem != nil {
|
||||
elem, err := c.getInterpretedValue(prefix, value.Elem)
|
||||
|
@ -999,7 +976,7 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
}
|
||||
return c.parseMakeInterface(underlying, value.Type, prefix)
|
||||
|
||||
case *MapValue:
|
||||
case *ir.MapValue:
|
||||
// Create initial bucket.
|
||||
firstBucketGlobal, keySize, valueSize, err := c.initMapNewBucket(prefix, value.Type)
|
||||
if err != nil {
|
||||
|
@ -1018,7 +995,7 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
return llvm.Value{}, nil
|
||||
}
|
||||
|
||||
constVal := key.(*ConstValue).Expr
|
||||
constVal := key.(*ir.ConstValue).Expr
|
||||
var keyBuf []byte
|
||||
switch constVal.Type().Underlying().(*types.Basic).Kind() {
|
||||
case types.String:
|
||||
|
@ -1079,7 +1056,7 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
hashmapPtr.SetLinkage(llvm.InternalLinkage)
|
||||
return llvm.ConstInBoundsGEP(hashmapPtr, []llvm.Value{zero}), nil
|
||||
|
||||
case *PointerBitCastValue:
|
||||
case *ir.PointerBitCastValue:
|
||||
elem, err := c.getInterpretedValue(prefix, value.Elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
|
@ -1090,14 +1067,14 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
}
|
||||
return llvm.ConstBitCast(elem, llvmType), nil
|
||||
|
||||
case *PointerToUintptrValue:
|
||||
case *ir.PointerToUintptrValue:
|
||||
elem, err := c.getInterpretedValue(prefix, value.Elem)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.ConstPtrToInt(elem, c.uintptrType), nil
|
||||
|
||||
case *PointerValue:
|
||||
case *ir.PointerValue:
|
||||
if value.Elem == nil {
|
||||
typ, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
|
@ -1119,7 +1096,7 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
ptr := llvm.ConstInBoundsGEP(elem, []llvm.Value{zero})
|
||||
return ptr, nil
|
||||
|
||||
case *SliceValue:
|
||||
case *ir.SliceValue:
|
||||
var globalPtr llvm.Value
|
||||
var arrayLength uint64
|
||||
if value.Array == nil {
|
||||
|
@ -1159,7 +1136,7 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
})
|
||||
return slice, nil
|
||||
|
||||
case *StructValue:
|
||||
case *ir.StructValue:
|
||||
fields := make([]llvm.Value, len(value.Fields))
|
||||
for i, elem := range value.Fields {
|
||||
field, err := c.getInterpretedValue(prefix, elem)
|
||||
|
@ -1181,7 +1158,7 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
return llvm.Value{}, errors.New("init: unknown struct type: " + value.Type.String())
|
||||
}
|
||||
|
||||
case *ZeroBasicValue:
|
||||
case *ir.ZeroBasicValue:
|
||||
llvmType, err := c.getLLVMType(value.Type)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
|
@ -1195,36 +1172,36 @@ func (c *Compiler) getInterpretedValue(prefix string, value Value) (llvm.Value,
|
|||
|
||||
func (c *Compiler) parseFunc(frame *Frame) error {
|
||||
if c.dumpSSA {
|
||||
fmt.Printf("\nfunc %s:\n", frame.fn.fn)
|
||||
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
|
||||
}
|
||||
if !frame.fn.IsExported() {
|
||||
frame.fn.llvmFn.SetLinkage(llvm.InternalLinkage)
|
||||
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
|
||||
}
|
||||
|
||||
if c.debug {
|
||||
pos := c.ir.program.Fset.Position(frame.fn.fn.Pos())
|
||||
pos := c.ir.Program.Fset.Position(frame.fn.Pos())
|
||||
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), frame.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
// Pre-create all basic blocks in the function.
|
||||
for _, block := range frame.fn.fn.DomPreorder() {
|
||||
llvmBlock := c.ctx.AddBasicBlock(frame.fn.llvmFn, block.Comment)
|
||||
for _, block := range frame.fn.DomPreorder() {
|
||||
llvmBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, block.Comment)
|
||||
frame.blocks[block] = llvmBlock
|
||||
}
|
||||
if frame.blocking {
|
||||
frame.cleanupBlock = c.ctx.AddBasicBlock(frame.fn.llvmFn, "task.cleanup")
|
||||
frame.suspendBlock = c.ctx.AddBasicBlock(frame.fn.llvmFn, "task.suspend")
|
||||
frame.cleanupBlock = c.ctx.AddBasicBlock(frame.fn.LLVMFn, "task.cleanup")
|
||||
frame.suspendBlock = c.ctx.AddBasicBlock(frame.fn.LLVMFn, "task.suspend")
|
||||
}
|
||||
entryBlock := frame.blocks[frame.fn.fn.Blocks[0]]
|
||||
entryBlock := frame.blocks[frame.fn.Blocks[0]]
|
||||
|
||||
// Load function parameters
|
||||
for i, param := range frame.fn.fn.Params {
|
||||
llvmParam := frame.fn.llvmFn.Param(frame.params[param])
|
||||
for i, param := range frame.fn.Params {
|
||||
llvmParam := frame.fn.LLVMFn.Param(frame.params[param])
|
||||
frame.locals[param] = llvmParam
|
||||
|
||||
// Add debug information to this parameter (if available)
|
||||
if c.debug && frame.fn.fn.Syntax() != nil {
|
||||
pos := c.ir.program.Fset.Position(frame.fn.fn.Syntax().Pos())
|
||||
if c.debug && frame.fn.Syntax() != nil {
|
||||
pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos())
|
||||
dityp, err := c.getDIType(param.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1243,16 +1220,16 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
|||
|
||||
// Load free variables from the context. This is a closure (or bound
|
||||
// method).
|
||||
if len(frame.fn.fn.FreeVars) != 0 {
|
||||
if len(frame.fn.FreeVars) != 0 {
|
||||
if !c.ir.FunctionNeedsContext(frame.fn) {
|
||||
panic("free variables on function without context")
|
||||
}
|
||||
c.builder.SetInsertPointAtEnd(entryBlock)
|
||||
context := frame.fn.llvmFn.Param(len(frame.fn.fn.Params))
|
||||
context := frame.fn.LLVMFn.Param(len(frame.fn.Params))
|
||||
|
||||
// Determine the context type. It's a struct containing all variables.
|
||||
freeVarTypes := make([]llvm.Type, 0, len(frame.fn.fn.FreeVars))
|
||||
for _, freeVar := range frame.fn.fn.FreeVars {
|
||||
freeVarTypes := make([]llvm.Type, 0, len(frame.fn.FreeVars))
|
||||
for _, freeVar := range frame.fn.FreeVars {
|
||||
typ, err := c.getLLVMType(freeVar.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1279,7 +1256,7 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
|||
// A free variable is always a pointer when this is a closure, but it
|
||||
// can be another type when it is a wrapper for a bound method (these
|
||||
// wrappers are generated by the ssa package).
|
||||
for i, freeVar := range frame.fn.fn.FreeVars {
|
||||
for i, freeVar := range frame.fn.FreeVars {
|
||||
indices := []llvm.Value{
|
||||
llvm.ConstInt(llvm.Int32Type(), 0, false),
|
||||
llvm.ConstInt(llvm.Int32Type(), uint64(i), false),
|
||||
|
@ -1291,7 +1268,7 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
|||
|
||||
c.builder.SetInsertPointAtEnd(entryBlock)
|
||||
|
||||
if frame.fn.fn.Recover != nil {
|
||||
if frame.fn.Recover != nil {
|
||||
// Create defer list pointer.
|
||||
deferType := llvm.PointerType(c.mod.GetTypeByName("runtime._defer"), 0)
|
||||
frame.deferPtr = c.builder.CreateAlloca(deferType, "deferPtr")
|
||||
|
@ -1322,7 +1299,7 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
|||
mem := c.builder.CreateCall(c.coroFreeFunc, []llvm.Value{id, frame.taskHandle}, "task.data.free")
|
||||
c.builder.CreateCall(c.freeFunc, []llvm.Value{mem}, "")
|
||||
// re-insert parent coroutine
|
||||
c.builder.CreateCall(c.mod.NamedFunction("runtime.yieldToScheduler"), []llvm.Value{frame.fn.llvmFn.FirstParam()}, "")
|
||||
c.builder.CreateCall(c.mod.NamedFunction("runtime.yieldToScheduler"), []llvm.Value{frame.fn.LLVMFn.FirstParam()}, "")
|
||||
c.builder.CreateBr(frame.suspendBlock)
|
||||
|
||||
// Coroutine suspend. A call to llvm.coro.suspend() will branch here.
|
||||
|
@ -1332,7 +1309,7 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
|||
}
|
||||
|
||||
// Fill blocks with instructions.
|
||||
for _, block := range frame.fn.fn.DomPreorder() {
|
||||
for _, block := range frame.fn.DomPreorder() {
|
||||
if c.dumpSSA {
|
||||
fmt.Printf("%s:\n", block.Comment)
|
||||
}
|
||||
|
@ -1354,7 +1331,7 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
if frame.fn.fn.Name() == "init" && len(block.Instrs) == 0 {
|
||||
if frame.fn.Name() == "init" && len(block.Instrs) == 0 {
|
||||
c.builder.CreateRetVoid()
|
||||
}
|
||||
}
|
||||
|
@ -1377,14 +1354,14 @@ func (c *Compiler) parseFunc(frame *Frame) error {
|
|||
|
||||
func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||
if c.debug {
|
||||
pos := c.ir.program.Fset.Position(instr.Pos())
|
||||
pos := c.ir.Program.Fset.Position(instr.Pos())
|
||||
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), frame.difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
switch instr := instr.(type) {
|
||||
case ssa.Value:
|
||||
value, err := c.parseExpr(frame, instr)
|
||||
if err == cgoWrapperError {
|
||||
if err == ir.ErrCGoWrapper {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return nil
|
||||
}
|
||||
|
@ -1551,7 +1528,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
|||
return nil
|
||||
} else {
|
||||
// Multiple return values. Put them all in a struct.
|
||||
retVal, err := getZeroValue(frame.fn.llvmFn.Type().ElementType().ReturnType())
|
||||
retVal, err := getZeroValue(frame.fn.LLVMFn.Type().ElementType().ReturnType())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1573,7 +1550,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
|||
return nil
|
||||
case *ssa.Store:
|
||||
llvmAddr, err := c.parseExpr(frame, instr.Addr)
|
||||
if err == cgoWrapperError {
|
||||
if err == ir.ErrCGoWrapper {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return nil
|
||||
}
|
||||
|
@ -1854,7 +1831,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
|
|||
}
|
||||
}
|
||||
targetFunc := c.ir.GetFunction(fn)
|
||||
if targetFunc.llvmFn.IsNil() {
|
||||
if targetFunc.LLVMFn.IsNil() {
|
||||
return llvm.Value{}, errors.New("undefined function: " + targetFunc.LinkName())
|
||||
}
|
||||
var context llvm.Value
|
||||
|
@ -1877,7 +1854,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
|
|||
}
|
||||
}
|
||||
}
|
||||
return c.parseFunctionCall(frame, instr.Args, targetFunc.llvmFn, context, c.ir.IsBlocking(targetFunc), parentHandle)
|
||||
return c.parseFunctionCall(frame, instr.Args, targetFunc.LLVMFn, context, c.ir.IsBlocking(targetFunc), parentHandle)
|
||||
}
|
||||
|
||||
// Builtin or function pointer.
|
||||
|
@ -1903,7 +1880,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
|
|||
}
|
||||
|
||||
func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value) {
|
||||
if frame.fn.nobounds {
|
||||
if frame.fn.IsNoBounds() {
|
||||
// The //go:nobounds pragma was added to the function to avoid bounds
|
||||
// checking.
|
||||
return
|
||||
|
@ -2016,7 +1993,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
return c.builder.CreateGEP(val, indices, ""), nil
|
||||
case *ssa.Function:
|
||||
fn := c.ir.GetFunction(expr)
|
||||
ptr := fn.llvmFn
|
||||
ptr := fn.LLVMFn
|
||||
if c.ir.FunctionNeedsContext(fn) {
|
||||
// Create closure for function pointer.
|
||||
// Closure is: {context, function pointer}
|
||||
|
@ -2029,9 +2006,9 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
case *ssa.Global:
|
||||
if strings.HasPrefix(expr.Name(), "__cgofn__cgo_") || strings.HasPrefix(expr.Name(), "_cgo_") {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return llvm.Value{}, cgoWrapperError
|
||||
return llvm.Value{}, ir.ErrCGoWrapper
|
||||
}
|
||||
value := c.ir.GetGlobal(expr).llvmGlobal
|
||||
value := c.ir.GetGlobal(expr).LLVMGlobal
|
||||
if value.IsNil() {
|
||||
return llvm.Value{}, errors.New("global not found: " + c.ir.GetGlobal(expr).LinkName())
|
||||
}
|
||||
|
@ -2209,7 +2186,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
elemSize := c.targetData.TypeAllocSize(llvmElemType)
|
||||
|
||||
// Bounds checking.
|
||||
if !frame.fn.nobounds {
|
||||
if !frame.fn.IsNoBounds() {
|
||||
sliceBoundsCheck := c.mod.NamedFunction("runtime.sliceBoundsCheckMake")
|
||||
c.builder.CreateCall(sliceBoundsCheck, []llvm.Value{sliceLen, sliceCap}, "")
|
||||
}
|
||||
|
@ -2343,7 +2320,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
sliceCap := c.builder.CreateSub(llvmLenInt, low, "slice.cap")
|
||||
|
||||
// This check is optimized away in most cases.
|
||||
if !frame.fn.nobounds {
|
||||
if !frame.fn.IsNoBounds() {
|
||||
sliceBoundsCheck := c.mod.NamedFunction("runtime.sliceBoundsCheck")
|
||||
c.builder.CreateCall(sliceBoundsCheck, []llvm.Value{llvmLen, low, high}, "")
|
||||
}
|
||||
|
@ -2372,7 +2349,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
high = oldLen
|
||||
}
|
||||
|
||||
if !frame.fn.nobounds {
|
||||
if !frame.fn.IsNoBounds() {
|
||||
sliceBoundsCheck := c.mod.NamedFunction("runtime.sliceBoundsCheck")
|
||||
c.builder.CreateCall(sliceBoundsCheck, []llvm.Value{oldLen, low, high}, "")
|
||||
}
|
||||
|
@ -2408,7 +2385,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
high = oldLen
|
||||
}
|
||||
|
||||
if !frame.fn.nobounds {
|
||||
if !frame.fn.IsNoBounds() {
|
||||
sliceBoundsCheck := c.mod.NamedFunction("runtime.sliceBoundsCheck")
|
||||
c.builder.CreateCall(sliceBoundsCheck, []llvm.Value{oldLen, low, high}, "")
|
||||
}
|
||||
|
@ -2491,8 +2468,8 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
// value.
|
||||
|
||||
prevBlock := c.builder.GetInsertBlock()
|
||||
okBlock := c.ctx.AddBasicBlock(frame.fn.llvmFn, "typeassert.ok")
|
||||
nextBlock := c.ctx.AddBasicBlock(frame.fn.llvmFn, "typeassert.next")
|
||||
okBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.ok")
|
||||
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.next")
|
||||
frame.blocks[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
c.builder.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||
|
||||
|
@ -3034,7 +3011,7 @@ func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.V
|
|||
|
||||
// Get the function signature type, which is a closure type.
|
||||
// A closure is a tuple of {context, function pointer}.
|
||||
typ, err := c.getLLVMType(f.fn.Signature)
|
||||
typ, err := c.getLLVMType(f.Signature)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
|
@ -3044,7 +3021,7 @@ func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.V
|
|||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
closure = c.builder.CreateInsertValue(closure, f.llvmFn, 1, "")
|
||||
closure = c.builder.CreateInsertValue(closure, f.LLVMFn, 1, "")
|
||||
closure = c.builder.CreateInsertValue(closure, context, 0, "")
|
||||
return closure, nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package ir
|
||||
|
||||
// This file provides functionality to interpret very basic Go SSA, for
|
||||
// compile-time initialization of globals.
|
||||
|
@ -14,6 +14,8 @@ import (
|
|||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
var ErrCGoWrapper = errors.New("tinygo internal: cgo wrapper") // a signal, not an error
|
||||
|
||||
// Ignore these calls (replace with a zero return value) when encountered during
|
||||
// interpretation.
|
||||
var ignoreInitCalls = map[string]struct{}{
|
||||
|
@ -33,7 +35,7 @@ func (p *Program) Interpret(block *ssa.BasicBlock, dumpSSA bool) error {
|
|||
}
|
||||
for {
|
||||
i, err := p.interpret(block.Instrs, nil, nil, nil, dumpSSA)
|
||||
if err == cgoWrapperError {
|
||||
if err == ErrCGoWrapper {
|
||||
// skip this instruction
|
||||
block.Instrs = block.Instrs[i+1:]
|
||||
continue
|
||||
|
@ -410,7 +412,7 @@ func (p *Program) getValue(value ssa.Value, locals map[ssa.Value]Value) (Value,
|
|||
case *ssa.Global:
|
||||
if strings.HasPrefix(value.Name(), "__cgofn__cgo_") || strings.HasPrefix(value.Name(), "_cgo_") {
|
||||
// Ignore CGo global variables which we don't use.
|
||||
return nil, cgoWrapperError
|
||||
return nil, ErrCGoWrapper
|
||||
}
|
||||
g := p.GetGlobal(value)
|
||||
if g.initializer == nil {
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package ir
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
@ -19,7 +19,7 @@ import (
|
|||
// View on all functions, types, and globals in a program, with analysis
|
||||
// results.
|
||||
type Program struct {
|
||||
program *ssa.Program
|
||||
Program *ssa.Program
|
||||
mainPkg *ssa.Package
|
||||
Functions []*Function
|
||||
functionMap map[*ssa.Function]*Function
|
||||
|
@ -38,8 +38,8 @@ type Program struct {
|
|||
|
||||
// Function or method.
|
||||
type Function struct {
|
||||
fn *ssa.Function
|
||||
llvmFn llvm.Value
|
||||
*ssa.Function
|
||||
LLVMFn llvm.Value
|
||||
linkName string // go:linkname or go:export pragma
|
||||
exported bool // go:export
|
||||
nobounds bool // go:nobounds pragma
|
||||
|
@ -52,9 +52,9 @@ type Function struct {
|
|||
|
||||
// Global variable, possibly constant.
|
||||
type Global struct {
|
||||
*ssa.Global
|
||||
program *Program
|
||||
g *ssa.Global
|
||||
llvmGlobal llvm.Value
|
||||
LLVMGlobal llvm.Value
|
||||
linkName string // go:extern
|
||||
extern bool // go:extern
|
||||
initializer Value
|
||||
|
@ -62,8 +62,8 @@ type Global struct {
|
|||
|
||||
// Type with a name and possibly methods.
|
||||
type NamedType struct {
|
||||
t *ssa.Type
|
||||
llvmType llvm.Type
|
||||
*ssa.Type
|
||||
LLVMType llvm.Type
|
||||
}
|
||||
|
||||
// Type that is at some point put in an interface.
|
||||
|
@ -110,7 +110,7 @@ func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
|||
program.Build()
|
||||
|
||||
return &Program{
|
||||
program: program,
|
||||
Program: program,
|
||||
mainPkg: program.ImportedPackage(mainPath),
|
||||
functionMap: make(map[*ssa.Function]*Function),
|
||||
globalMap: make(map[*ssa.Global]*Global),
|
||||
|
@ -141,7 +141,7 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
|||
}
|
||||
p.addFunction(member)
|
||||
case *ssa.Type:
|
||||
t := &NamedType{t: member}
|
||||
t := &NamedType{Type: member}
|
||||
p.NamedTypes = append(p.NamedTypes, t)
|
||||
methods := getAllMethods(pkg.Prog, member.Type())
|
||||
if !types.IsInterface(member.Type()) {
|
||||
|
@ -151,8 +151,8 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
|||
}
|
||||
}
|
||||
case *ssa.Global:
|
||||
g := &Global{program: p, g: member}
|
||||
doc := p.comments[g.g.RelString(nil)]
|
||||
g := &Global{program: p, Global: member}
|
||||
doc := p.comments[g.RelString(nil)]
|
||||
if doc != nil {
|
||||
g.parsePragmas(doc)
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ func (p *Program) AddPackage(pkg *ssa.Package) {
|
|||
}
|
||||
|
||||
func (p *Program) addFunction(ssaFn *ssa.Function) {
|
||||
f := &Function{fn: ssaFn}
|
||||
f := &Function{Function: ssaFn}
|
||||
f.parsePragmas()
|
||||
p.Functions = append(p.Functions, f)
|
||||
p.functionMap[ssaFn] = f
|
||||
|
@ -207,12 +207,16 @@ func (p *Program) SortFuncs(funcs []*types.Func) {
|
|||
sort.Sort(m)
|
||||
}
|
||||
|
||||
func (p *Program) MainPkg() *ssa.Package {
|
||||
return p.mainPkg
|
||||
}
|
||||
|
||||
// Parse compiler directives in the preceding comments.
|
||||
func (f *Function) parsePragmas() {
|
||||
if f.fn.Syntax() == nil {
|
||||
if f.Syntax() == nil {
|
||||
return
|
||||
}
|
||||
if decl, ok := f.fn.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
||||
if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil {
|
||||
for _, comment := range decl.Doc.List {
|
||||
if !strings.HasPrefix(comment.Text, "//go:") {
|
||||
continue
|
||||
|
@ -220,14 +224,14 @@ func (f *Function) parsePragmas() {
|
|||
parts := strings.Fields(comment.Text)
|
||||
switch parts[0] {
|
||||
case "//go:linkname":
|
||||
if len(parts) != 3 || parts[1] != f.fn.Name() {
|
||||
if len(parts) != 3 || parts[1] != f.Name() {
|
||||
continue
|
||||
}
|
||||
// Only enable go:linkname when the package imports "unsafe".
|
||||
// This is a slightly looser requirement than what gc uses: gc
|
||||
// requires the file to import "unsafe", not the package as a
|
||||
// whole.
|
||||
if hasUnsafeImport(f.fn.Pkg.Pkg) {
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
f.linkName = parts[2]
|
||||
}
|
||||
case "//go:nobounds":
|
||||
|
@ -235,7 +239,7 @@ func (f *Function) parsePragmas() {
|
|||
// runtime functions.
|
||||
// This is somewhat dangerous and thus only imported in packages
|
||||
// that import unsafe.
|
||||
if hasUnsafeImport(f.fn.Pkg.Pkg) {
|
||||
if hasUnsafeImport(f.Pkg.Pkg) {
|
||||
f.nobounds = true
|
||||
}
|
||||
case "//go:export":
|
||||
|
@ -249,6 +253,10 @@ func (f *Function) parsePragmas() {
|
|||
}
|
||||
}
|
||||
|
||||
func (f *Function) IsNoBounds() bool {
|
||||
return f.nobounds
|
||||
}
|
||||
|
||||
// Return true iff this function is externally visible.
|
||||
func (f *Function) IsExported() bool {
|
||||
return f.exported
|
||||
|
@ -259,16 +267,16 @@ func (f *Function) LinkName() string {
|
|||
if f.linkName != "" {
|
||||
return f.linkName
|
||||
}
|
||||
if f.fn.Signature.Recv() != nil {
|
||||
if f.Signature.Recv() != nil {
|
||||
// Method on a defined type (which may be a pointer).
|
||||
return f.fn.RelString(nil)
|
||||
return f.RelString(nil)
|
||||
} else {
|
||||
// Bare function.
|
||||
if name := f.CName(); name != "" {
|
||||
// Name CGo functions directly.
|
||||
return name
|
||||
} else {
|
||||
return f.fn.RelString(nil)
|
||||
return f.RelString(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,7 +284,7 @@ func (f *Function) LinkName() string {
|
|||
// 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()
|
||||
name := f.Name()
|
||||
if strings.HasPrefix(name, "_Cfunc_") {
|
||||
return name[len("_Cfunc_"):]
|
||||
}
|
||||
|
@ -305,13 +313,17 @@ func (g *Global) LinkName() string {
|
|||
if g.linkName != "" {
|
||||
return g.linkName
|
||||
}
|
||||
return g.g.RelString(nil)
|
||||
return g.RelString(nil)
|
||||
}
|
||||
|
||||
func (g *Global) IsExtern() bool {
|
||||
return g.extern
|
||||
}
|
||||
|
||||
func (g *Global) Initializer() Value {
|
||||
return g.initializer
|
||||
}
|
||||
|
||||
// Wrapper type to implement sort.Interface for []*types.Selection.
|
||||
type methodList struct {
|
||||
methods []*types.Selection
|
||||
|
@ -351,3 +363,25 @@ func (fl *funcList) Less(i, j int) bool {
|
|||
func (fl *funcList) Swap(i, j int) {
|
||||
fl.funcs[i], fl.funcs[j] = fl.funcs[j], fl.funcs[i]
|
||||
}
|
||||
|
||||
// Return true if this is a CGo-internal function that can be ignored.
|
||||
func isCGoInternal(name string) bool {
|
||||
if strings.HasPrefix(name, "_Cgo_") || strings.HasPrefix(name, "_cgo") {
|
||||
// _Cgo_ptr, _Cgo_use, _cgoCheckResult, _cgo_runtime_cgocall
|
||||
return true // CGo-internal functions
|
||||
}
|
||||
if strings.HasPrefix(name, "__cgofn__cgo_") {
|
||||
return true // CGo function pointer in global scope
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get all methods of a type.
|
||||
func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
|
||||
ms := prog.MethodSets.MethodSet(typ)
|
||||
methods := make([]*types.Selection, ms.Len())
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
methods[i] = ms.At(i)
|
||||
}
|
||||
return methods
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package ir
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
|
@ -81,7 +81,7 @@ func (p *Program) AnalyseCallgraph() {
|
|||
f.children = nil
|
||||
f.parents = nil
|
||||
|
||||
for _, block := range f.fn.Blocks {
|
||||
for _, block := range f.Blocks {
|
||||
for _, instr := range block.Instrs {
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.Call:
|
||||
|
@ -99,7 +99,7 @@ func (p *Program) AnalyseCallgraph() {
|
|||
if child.CName() != "" {
|
||||
continue // assume non-blocking
|
||||
}
|
||||
if child.fn.RelString(nil) == "time.Sleep" {
|
||||
if child.RelString(nil) == "time.Sleep" {
|
||||
f.blocking = true
|
||||
}
|
||||
f.children = append(f.children, child)
|
||||
|
@ -122,11 +122,11 @@ func (p *Program) AnalyseInterfaceConversions() {
|
|||
p.typesWithMethods = map[string]*TypeWithMethods{}
|
||||
|
||||
for _, f := range p.Functions {
|
||||
for _, block := range f.fn.Blocks {
|
||||
for _, block := range f.Blocks {
|
||||
for _, instr := range block.Instrs {
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.MakeInterface:
|
||||
methods := getAllMethods(f.fn.Prog, instr.X.Type())
|
||||
methods := getAllMethods(f.Prog, instr.X.Type())
|
||||
name := instr.X.Type().String()
|
||||
if _, ok := p.typesWithMethods[name]; !ok && len(methods) > 0 {
|
||||
t := &TypeWithMethods{
|
||||
|
@ -155,7 +155,7 @@ func (p *Program) AnalyseFunctionPointers() {
|
|||
p.fpWithContext = map[string]struct{}{}
|
||||
|
||||
for _, f := range p.Functions {
|
||||
for _, block := range f.fn.Blocks {
|
||||
for _, block := range f.Blocks {
|
||||
for _, instr := range block.Instrs {
|
||||
switch instr := instr.(type) {
|
||||
case ssa.CallInstruction:
|
||||
|
@ -229,7 +229,7 @@ func (p *Program) AnalyseBlockingRecursive() {
|
|||
func (p *Program) AnalyseGoCalls() {
|
||||
p.goCalls = nil
|
||||
for _, f := range p.Functions {
|
||||
for _, block := range f.fn.Blocks {
|
||||
for _, block := range f.Blocks {
|
||||
for _, instr := range block.Instrs {
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.Go:
|
||||
|
@ -262,16 +262,16 @@ func (p *Program) SimpleDCE() {
|
|||
// Initial set of live functions. Include main.main, *.init and runtime.*
|
||||
// functions.
|
||||
main := p.mainPkg.Members["main"].(*ssa.Function)
|
||||
runtimePkg := p.program.ImportedPackage("runtime")
|
||||
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()) {
|
||||
if f.Synthetic == "package initializer" || f.Pkg == runtimePkg {
|
||||
if f.flag || isCGoInternal(f.Name()) {
|
||||
continue
|
||||
}
|
||||
f.flag = true
|
||||
worklist = append(worklist, f.fn)
|
||||
worklist = append(worklist, f.Function)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,8 +282,8 @@ func (p *Program) SimpleDCE() {
|
|||
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)
|
||||
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?
|
||||
|
@ -292,7 +292,7 @@ func (p *Program) SimpleDCE() {
|
|||
}
|
||||
if !callee.flag {
|
||||
callee.flag = true
|
||||
worklist = append(worklist, callee.fn)
|
||||
worklist = append(worklist, callee.Function)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -325,7 +325,7 @@ func (p *Program) SimpleDCE() {
|
|||
if f.flag {
|
||||
livefunctions = append(livefunctions, f)
|
||||
} else {
|
||||
delete(p.functionMap, f.fn)
|
||||
delete(p.functionMap, f.Function)
|
||||
}
|
||||
}
|
||||
p.Functions = livefunctions
|
||||
|
@ -418,7 +418,7 @@ func (p *Program) FunctionNeedsContext(f *Function) bool {
|
|||
if !f.addressTaken {
|
||||
return false
|
||||
}
|
||||
return p.SignatureNeedsContext(f.fn.Signature)
|
||||
return p.SignatureNeedsContext(f.Signature)
|
||||
}
|
||||
|
||||
func (p *Program) SignatureNeedsContext(sig *types.Signature) bool {
|
Загрузка…
Создание таблицы
Сослаться в новой задаче