compiler: refactor IR parts into separate package

Этот коммит содержится в:
Ayke van Laethem 2018-09-22 20:25:50 +02:00
родитель 473e71b573
коммит b75a02e66d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
5 изменённых файлов: 182 добавлений и 169 удалений

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

@ -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

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

@ -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 {