compiler: implement func value and builtin defers
Co-authored-by: Justin A. Wilson <maker.pro.game@gmail.com>
Этот коммит содержится в:
родитель
3650c2c739
коммит
848c3e55a9
4 изменённых файлов: 167 добавлений и 14 удалений
|
@ -74,7 +74,14 @@ type builder struct {
|
||||||
deferFuncs map[*ir.Function]int
|
deferFuncs map[*ir.Function]int
|
||||||
deferInvokeFuncs map[string]int
|
deferInvokeFuncs map[string]int
|
||||||
deferClosureFuncs map[*ir.Function]int
|
deferClosureFuncs map[*ir.Function]int
|
||||||
|
deferExprFuncs map[ssa.Value]int
|
||||||
selectRecvBuf map[*ssa.Select]llvm.Value
|
selectRecvBuf map[*ssa.Select]llvm.Value
|
||||||
|
deferBuiltinFuncs map[ssa.Value]deferBuiltin
|
||||||
|
}
|
||||||
|
|
||||||
|
type deferBuiltin struct {
|
||||||
|
funcName string
|
||||||
|
callback int
|
||||||
}
|
}
|
||||||
|
|
||||||
type phiNode struct {
|
type phiNode struct {
|
||||||
|
|
|
@ -16,6 +16,7 @@ package compiler
|
||||||
import (
|
import (
|
||||||
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
"github.com/tinygo-org/tinygo/compiler/llvmutil"
|
||||||
"github.com/tinygo-org/tinygo/ir"
|
"github.com/tinygo-org/tinygo/ir"
|
||||||
|
"go/types"
|
||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
@ -28,6 +29,8 @@ func (b *builder) deferInitFunc() {
|
||||||
b.deferFuncs = make(map[*ir.Function]int)
|
b.deferFuncs = make(map[*ir.Function]int)
|
||||||
b.deferInvokeFuncs = make(map[string]int)
|
b.deferInvokeFuncs = make(map[string]int)
|
||||||
b.deferClosureFuncs = make(map[*ir.Function]int)
|
b.deferClosureFuncs = make(map[*ir.Function]int)
|
||||||
|
b.deferExprFuncs = make(map[ssa.Value]int)
|
||||||
|
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
|
||||||
|
|
||||||
// Create defer list pointer.
|
// Create defer list pointer.
|
||||||
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
|
||||||
|
@ -151,9 +154,54 @@ func (b *builder) createDefer(instr *ssa.Defer) {
|
||||||
values = append(values, context)
|
values = append(values, context)
|
||||||
valueTypes = append(valueTypes, context.Type())
|
valueTypes = append(valueTypes, context.Type())
|
||||||
|
|
||||||
|
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
|
||||||
|
var funcName string
|
||||||
|
switch builtin.Name() {
|
||||||
|
case "close":
|
||||||
|
funcName = "chanClose"
|
||||||
|
default:
|
||||||
|
b.addError(instr.Pos(), "todo: Implement defer for "+builtin.Name())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
|
||||||
|
b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
|
||||||
|
funcName,
|
||||||
|
len(b.allDeferFuncs),
|
||||||
|
}
|
||||||
|
b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value)
|
||||||
|
}
|
||||||
|
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferBuiltinFuncs[instr.Call.Value].callback), false)
|
||||||
|
|
||||||
|
// Collect all values to be put in the struct (starting with
|
||||||
|
// runtime._defer fields).
|
||||||
|
values = []llvm.Value{callback, next}
|
||||||
|
for _, param := range instr.Call.Args {
|
||||||
|
llvmParam := b.getValue(param)
|
||||||
|
values = append(values, llvmParam)
|
||||||
|
valueTypes = append(valueTypes, llvmParam.Type())
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
b.addError(instr.Pos(), "todo: defer on uncommon function call type")
|
funcValue := b.getValue(instr.Call.Value)
|
||||||
return
|
|
||||||
|
if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok {
|
||||||
|
b.deferExprFuncs[instr.Call.Value] = len(b.allDeferFuncs)
|
||||||
|
b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call)
|
||||||
|
}
|
||||||
|
|
||||||
|
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferExprFuncs[instr.Call.Value]), false)
|
||||||
|
|
||||||
|
// Collect all values to be put in the struct (starting with
|
||||||
|
// runtime._defer fields, followed by all parameters including the
|
||||||
|
// context pointer).
|
||||||
|
values = []llvm.Value{callback, next, funcValue}
|
||||||
|
valueTypes = append(valueTypes, funcValue.Type())
|
||||||
|
for _, param := range instr.Call.Args {
|
||||||
|
llvmParam := b.getValue(param)
|
||||||
|
values = append(values, llvmParam)
|
||||||
|
valueTypes = append(valueTypes, llvmParam.Type())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a struct out of the collected values to put in the defer frame.
|
// Make a struct out of the collected values to put in the defer frame.
|
||||||
|
@ -243,16 +291,23 @@ func (b *builder) createRunDefers() {
|
||||||
b.SetInsertPointAtEnd(block)
|
b.SetInsertPointAtEnd(block)
|
||||||
switch callback := callback.(type) {
|
switch callback := callback.(type) {
|
||||||
case *ssa.CallCommon:
|
case *ssa.CallCommon:
|
||||||
// Call on an interface value.
|
// Call on an value or interface value.
|
||||||
if !callback.IsInvoke() {
|
|
||||||
panic("expected an invoke call, not a direct call")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the real defer struct type and cast to it.
|
// Get the real defer struct type and cast to it.
|
||||||
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0), b.uintptrType, b.i8ptrType}
|
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||||
|
|
||||||
|
if !callback.IsInvoke() {
|
||||||
|
//Expect funcValue to be passed through the defer frame.
|
||||||
|
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
|
||||||
|
} else {
|
||||||
|
//Expect typecode
|
||||||
|
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
|
||||||
|
}
|
||||||
|
|
||||||
for _, arg := range callback.Args {
|
for _, arg := range callback.Args {
|
||||||
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
|
||||||
}
|
}
|
||||||
|
|
||||||
deferFrameType := b.ctx.StructType(valueTypes, false)
|
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||||
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||||
|
|
||||||
|
@ -265,18 +320,34 @@ func (b *builder) createRunDefers() {
|
||||||
forwardParams = append(forwardParams, forwardParam)
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Isolate the typecode.
|
var fnPtr llvm.Value
|
||||||
typecode, forwardParams := forwardParams[0], forwardParams[1:]
|
|
||||||
|
|
||||||
// Add the context parameter. An interface call cannot also be a
|
if !callback.IsInvoke() {
|
||||||
// closure but we have to supply the parameter anyway for platforms
|
// Isolate the func value.
|
||||||
// with a strict calling convention.
|
funcValue := forwardParams[0]
|
||||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
forwardParams = forwardParams[1:]
|
||||||
|
|
||||||
|
//Get function pointer and context
|
||||||
|
fp, context := b.decodeFuncValue(funcValue, callback.Signature())
|
||||||
|
fnPtr = fp
|
||||||
|
|
||||||
|
//Pass context
|
||||||
|
forwardParams = append(forwardParams, context)
|
||||||
|
} else {
|
||||||
|
// Isolate the typecode.
|
||||||
|
typecode := forwardParams[0]
|
||||||
|
forwardParams = forwardParams[1:]
|
||||||
|
fnPtr = b.getInvokePtr(callback, typecode)
|
||||||
|
|
||||||
|
// Add the context parameter. An interface call cannot also be a
|
||||||
|
// closure but we have to supply the parameter anyway for platforms
|
||||||
|
// with a strict calling convention.
|
||||||
|
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||||
|
}
|
||||||
|
|
||||||
// Parent coroutine handle.
|
// Parent coroutine handle.
|
||||||
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
|
||||||
|
|
||||||
fnPtr := b.getInvokePtr(callback, typecode)
|
|
||||||
b.createCall(fnPtr, forwardParams, "")
|
b.createCall(fnPtr, forwardParams, "")
|
||||||
|
|
||||||
case *ir.Function:
|
case *ir.Function:
|
||||||
|
@ -339,7 +410,31 @@ func (b *builder) createRunDefers() {
|
||||||
|
|
||||||
// Call deferred function.
|
// Call deferred function.
|
||||||
b.createCall(fn.LLVMFn, forwardParams, "")
|
b.createCall(fn.LLVMFn, forwardParams, "")
|
||||||
|
case *ssa.Builtin:
|
||||||
|
db := b.deferBuiltinFuncs[callback]
|
||||||
|
|
||||||
|
//Get parameter types
|
||||||
|
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
|
||||||
|
|
||||||
|
//Get signature from call results
|
||||||
|
params := callback.Type().Underlying().(*types.Signature).Params()
|
||||||
|
for i := 0; i < params.Len(); i++ {
|
||||||
|
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
deferFrameType := b.ctx.StructType(valueTypes, false)
|
||||||
|
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
|
||||||
|
|
||||||
|
// Extract the params from the struct.
|
||||||
|
var forwardParams []llvm.Value
|
||||||
|
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
||||||
|
for i := 0; i < params.Len(); i++ {
|
||||||
|
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
|
||||||
|
forwardParam := b.CreateLoad(gep, "param")
|
||||||
|
forwardParams = append(forwardParams, forwardParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.createRuntimeCall(db.funcName, forwardParams, "")
|
||||||
default:
|
default:
|
||||||
panic("unknown deferred function type")
|
panic("unknown deferred function type")
|
||||||
}
|
}
|
||||||
|
|
49
testdata/calls.go
предоставленный
49
testdata/calls.go
предоставленный
|
@ -44,6 +44,12 @@ func main() {
|
||||||
// defers in loop
|
// defers in loop
|
||||||
testDeferLoop()
|
testDeferLoop()
|
||||||
|
|
||||||
|
//defer func variable call
|
||||||
|
testDeferFuncVar()
|
||||||
|
|
||||||
|
//More complicated func variable call
|
||||||
|
testMultiFuncVar()
|
||||||
|
|
||||||
// Take a bound method and use it as a function pointer.
|
// Take a bound method and use it as a function pointer.
|
||||||
// This function pointer needs a context pointer.
|
// This function pointer needs a context pointer.
|
||||||
testBound(thing.String)
|
testBound(thing.String)
|
||||||
|
@ -64,6 +70,9 @@ func main() {
|
||||||
|
|
||||||
// regression testing
|
// regression testing
|
||||||
regression1033()
|
regression1033()
|
||||||
|
|
||||||
|
//Test deferred builtins
|
||||||
|
testDeferBuiltin()
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFunc(f func(int), arg int) {
|
func runFunc(f func(int), arg int) {
|
||||||
|
@ -91,6 +100,8 @@ func testDefer() {
|
||||||
defer t.Print("bar")
|
defer t.Print("bar")
|
||||||
|
|
||||||
println("deferring...")
|
println("deferring...")
|
||||||
|
d := dumb{}
|
||||||
|
defer d.Value(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDeferLoop() {
|
func testDeferLoop() {
|
||||||
|
@ -99,6 +110,30 @@ func testDeferLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDeferFuncVar() {
|
||||||
|
dummy, f := deferFunc()
|
||||||
|
dummy++
|
||||||
|
defer f(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMultiFuncVar() {
|
||||||
|
f := multiFuncDefer()
|
||||||
|
defer f(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDeferBuiltin() {
|
||||||
|
i := make(chan int)
|
||||||
|
defer close(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dumb struct {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*dumb) Value(key interface{}) interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func deferred(msg string, i int) {
|
func deferred(msg string, i int) {
|
||||||
println(msg, i)
|
println(msg, i)
|
||||||
}
|
}
|
||||||
|
@ -108,6 +143,20 @@ func exportedDefer() {
|
||||||
println("...exported defer")
|
println("...exported defer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deferFunc() (int, func(int)) {
|
||||||
|
return 0, func(i int){println("...extracted defer func ", i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func multiFuncDefer() func(int) {
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
if i > 0 {
|
||||||
|
return func(i int){println("Should not have gotten here. i = ", i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(i int){println("Called the correct function. i = ", i)}
|
||||||
|
}
|
||||||
|
|
||||||
func testBound(f func() string) {
|
func testBound(f func() string) {
|
||||||
println("bound method:", f())
|
println("bound method:", f())
|
||||||
}
|
}
|
||||||
|
|
2
testdata/calls.txt
предоставленный
2
testdata/calls.txt
предоставленный
|
@ -9,6 +9,8 @@ loop 3
|
||||||
loop 2
|
loop 2
|
||||||
loop 1
|
loop 1
|
||||||
loop 0
|
loop 0
|
||||||
|
...extracted defer func 1
|
||||||
|
Called the correct function. i = 1
|
||||||
bound method: foo
|
bound method: foo
|
||||||
thing inside closure: foo
|
thing inside closure: foo
|
||||||
inside fp closure: foo 3
|
inside fp closure: foo 3
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче