From d3b8364f260d29c623946be7518fec648b8f43e3 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Tue, 23 Jun 2020 08:13:59 -0500 Subject: [PATCH 01/15] Implemented ssa.Extract and ssa.Call cases for defer calls. --- compiler/compiler.go | 7 +++ compiler/defer.go | 107 ++++++++++++++++++++++++++++++++++++++++++- testdata/calls.go | 31 +++++++++++++ 3 files changed, 143 insertions(+), 2 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index fe45eb3fc6..c3ad896fa0 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -74,9 +74,16 @@ type builder struct { deferFuncs map[*ir.Function]int deferInvokeFuncs map[string]int deferClosureFuncs map[*ir.Function]int + deferExprFuncs map[interface{}]deferExpr selectRecvBuf map[*ssa.Select]llvm.Value } +type deferExpr struct { + funcValueSig llvm.Value + signature *types.Signature + callback int +} + type phiNode struct { ssa *ssa.Phi llvm llvm.Value diff --git a/compiler/defer.go b/compiler/defer.go index 4a2a480a81..3f9b221926 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -14,8 +14,10 @@ package compiler // frames. import ( + "fmt" "github.com/tinygo-org/tinygo/compiler/llvmutil" "github.com/tinygo-org/tinygo/ir" + "go/types" "golang.org/x/tools/go/ssa" "tinygo.org/x/go-llvm" ) @@ -28,6 +30,7 @@ func (b *builder) deferInitFunc() { b.deferFuncs = make(map[*ir.Function]int) b.deferInvokeFuncs = make(map[string]int) b.deferClosureFuncs = make(map[*ir.Function]int) + b.deferExprFuncs = make(map[interface{}]deferExpr) // Create defer list pointer. deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0) @@ -151,9 +154,79 @@ func (b *builder) createDefer(instr *ssa.Defer) { values = append(values, context) valueTypes = append(valueTypes, context.Type()) + } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { + b.addError(instr.Pos(), fmt.Sprintf("%+v", builtin)) + b.addError(instr.Pos(), "todo: Is builtin") + /*} else if extract, ok := instr.Call.Value.(*ssa.Extract); ok { + value := b.getValue(extract.Tuple) + funcValue := b.CreateExtractValue(value, extract.Index, "") + context := b.CreateExtractValue(funcValue, 0, "") + + callback := llvm.ConstInt(b.uintptrType, uint64(len(b.allDeferFuncs)), false) + b.allDeferFuncs = append(b.allDeferFuncs, extract) + + // 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} + for _, param := range instr.Call.Args { + llvmParam := b.getValue(param) + values = append(values, llvmParam) + valueTypes = append(valueTypes, llvmParam.Type()) + } + + values = append(values, context) + valueTypes = append(valueTypes, context.Type())*/ + } else if unop, ok := instr.Call.Value.(*ssa.UnOp); ok { + b.addError(instr.Pos(), fmt.Sprintf("%+v", unop)) + b.addError(instr.Pos(), "todo: Is UnOp") + /*} else if call, ok := instr.Call.Value.(*ssa.Call); ok { + funcValue := b.getValue(call.Call.Value)*/ + } else { - b.addError(instr.Pos(), "todo: defer on uncommon function call type") - return + var funcValue llvm.Value + var sig *types.Signature + + if extract, ok := instr.Call.Value.(*ssa.Extract); ok { + value := b.getValue(extract.Tuple) + funcValue = b.CreateExtractValue(value, extract.Index, "") + sig = extract.Tuple.(*ssa.Call).Call.Value.(*ssa.Function).Signature.Results().At(extract.Index).Type().(*types.Signature) + } else if call, ok := instr.Call.Value.(*ssa.Call); ok { + funcValue = b.getValue(call) + sig = call.Call.Value.Type().Underlying().(*types.Signature).Results().At(0).Type().(*types.Signature) + } + + if funcValue.IsNil() == false && sig != nil { + funcSig, context := b.decodeFuncValue(funcValue, sig) + + if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok { + b.deferExprFuncs[instr.Call.Value] = deferExpr{ + funcValueSig: funcSig, + signature: sig, + callback: len(b.allDeferFuncs), + } + b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value) + } + + callback := llvm.ConstInt(b.uintptrType, uint64(b.deferExprFuncs[instr.Call.Value].callback), 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} + for _, param := range instr.Call.Args { + llvmParam := b.getValue(param) + values = append(values, llvmParam) + valueTypes = append(valueTypes, llvmParam.Type()) + } + + values = append(values, context) + valueTypes = append(valueTypes, context.Type()) + } else { + b.addError(instr.Pos(), "todo: defer on uncommon function call type") + return + } } // Make a struct out of the collected values to put in the defer frame. @@ -339,6 +412,36 @@ func (b *builder) createRunDefers() { // Call deferred function. b.createCall(fn.LLVMFn, forwardParams, "") + case *ssa.Extract, *ssa.Call: + expr := b.deferExprFuncs[callback] + + // Get the real defer struct type and cast to it. + valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} + + //Get signature from call results + params := expr.signature.Params() + for i := 0; i < params.Len(); i++ { + valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type())) + } + + valueTypes = append(valueTypes, b.i8ptrType) // closure + 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 := 2; i < len(valueTypes); i++ { + gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") + forwardParam := b.CreateLoad(gep, "param") + forwardParams = append(forwardParams, forwardParam) + } + + // Parent coroutine handle. + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) + + // Call deferred function. + b.createCall(expr.funcValueSig, forwardParams, "") default: panic("unknown deferred function type") diff --git a/testdata/calls.go b/testdata/calls.go index 9818cdb789..fd7df4915c 100644 --- a/testdata/calls.go +++ b/testdata/calls.go @@ -44,6 +44,12 @@ func main() { // defers in loop testDeferLoop() + //defer func variable call + testDeferFuncVar() + + //More complicated func variable call + testMultiFuncVar() + // Take a bound method and use it as a function pointer. // This function pointer needs a context pointer. testBound(thing.String) @@ -99,6 +105,17 @@ func testDeferLoop() { } } +func testDeferFuncVar() { + dummy, f := deferFunc() + dummy++ + defer f(1) +} + +func testMultiFuncVar() { + f := multiFuncDefer() + defer f(1) +} + func deferred(msg string, i int) { println(msg, i) } @@ -108,6 +125,20 @@ func exportedDefer() { 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) { println("bound method:", f()) } From dc279a20686eb7d216afcb94f3f8ed415e31b1f3 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Tue, 23 Jun 2020 08:54:34 -0500 Subject: [PATCH 02/15] Removed commented out code --- compiler/defer.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/compiler/defer.go b/compiler/defer.go index 3f9b221926..8a79684e14 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -157,33 +157,9 @@ func (b *builder) createDefer(instr *ssa.Defer) { } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { b.addError(instr.Pos(), fmt.Sprintf("%+v", builtin)) b.addError(instr.Pos(), "todo: Is builtin") - /*} else if extract, ok := instr.Call.Value.(*ssa.Extract); ok { - value := b.getValue(extract.Tuple) - funcValue := b.CreateExtractValue(value, extract.Index, "") - context := b.CreateExtractValue(funcValue, 0, "") - - callback := llvm.ConstInt(b.uintptrType, uint64(len(b.allDeferFuncs)), false) - b.allDeferFuncs = append(b.allDeferFuncs, extract) - - // 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} - for _, param := range instr.Call.Args { - llvmParam := b.getValue(param) - values = append(values, llvmParam) - valueTypes = append(valueTypes, llvmParam.Type()) - } - - values = append(values, context) - valueTypes = append(valueTypes, context.Type())*/ } else if unop, ok := instr.Call.Value.(*ssa.UnOp); ok { b.addError(instr.Pos(), fmt.Sprintf("%+v", unop)) b.addError(instr.Pos(), "todo: Is UnOp") - /*} else if call, ok := instr.Call.Value.(*ssa.Call); ok { - funcValue := b.getValue(call.Call.Value)*/ - } else { var funcValue llvm.Value var sig *types.Signature From 25109908e2fe7123c06058093652f15f59903a26 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Wed, 24 Jun 2020 20:09:34 -0500 Subject: [PATCH 03/15] Implemented defer for 'close' built-in function --- compiler/compiler.go | 6 +++ compiler/defer.go | 92 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index c3ad896fa0..f437a0cbfa 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -76,6 +76,7 @@ type builder struct { deferClosureFuncs map[*ir.Function]int deferExprFuncs map[interface{}]deferExpr selectRecvBuf map[*ssa.Select]llvm.Value + deferBuiltinFuncs map[interface{}]deferBuiltin } type deferExpr struct { @@ -84,6 +85,11 @@ type deferExpr struct { callback int } +type deferBuiltin struct { + funcName string + callback int +} + type phiNode struct { ssa *ssa.Phi llvm llvm.Value diff --git a/compiler/defer.go b/compiler/defer.go index 8a79684e14..88d6611431 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -31,6 +31,7 @@ func (b *builder) deferInitFunc() { b.deferInvokeFuncs = make(map[string]int) b.deferClosureFuncs = make(map[*ir.Function]int) b.deferExprFuncs = make(map[interface{}]deferExpr) + b.deferBuiltinFuncs = make(map[interface{}]deferBuiltin) // Create defer list pointer. deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0) @@ -155,22 +156,53 @@ func (b *builder) createDefer(instr *ssa.Defer) { valueTypes = append(valueTypes, context.Type()) } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { - b.addError(instr.Pos(), fmt.Sprintf("%+v", builtin)) - b.addError(instr.Pos(), "todo: Is builtin") - } else if unop, ok := instr.Call.Value.(*ssa.UnOp); ok { - b.addError(instr.Pos(), fmt.Sprintf("%+v", unop)) - b.addError(instr.Pos(), "todo: Is UnOp") + var funcName string + switch builtin.Name() { + case "close": + funcName = "chanClose" + default: + b.addError(instr.Pos(), fmt.Sprint("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 { var funcValue llvm.Value var sig *types.Signature - if extract, ok := instr.Call.Value.(*ssa.Extract); ok { - value := b.getValue(extract.Tuple) - funcValue = b.CreateExtractValue(value, extract.Index, "") - sig = extract.Tuple.(*ssa.Call).Call.Value.(*ssa.Function).Signature.Results().At(extract.Index).Type().(*types.Signature) - } else if call, ok := instr.Call.Value.(*ssa.Call); ok { - funcValue = b.getValue(call) - sig = call.Call.Value.Type().Underlying().(*types.Signature).Results().At(0).Type().(*types.Signature) + switch expr := instr.Call.Value.(type) { + case *ssa.Extract: + value := b.getValue(expr.Tuple) + funcValue = b.CreateExtractValue(value, expr.Index, "") + sig = expr.Tuple.(*ssa.Call).Call.Value.(*ssa.Function).Signature.Results().At(expr.Index).Type().Underlying().(*types.Signature) + case *ssa.Call: + funcValue = b.getValue(expr) + sig = expr.Call.Value.Type().Underlying().(*types.Signature).Results().At(0).Type().Underlying().(*types.Signature) + case *ssa.UnOp: + funcValue = b.getValue(expr) + switch ty := expr.X.Type().(type) { + case *types.Pointer: + sig = ty.Elem().Underlying().(*types.Signature) + default: + sig = ty.Underlying().(*types.Signature).Results().At(0).Type().Underlying().(*types.Signature) + } } if funcValue.IsNil() == false && sig != nil { @@ -388,7 +420,7 @@ func (b *builder) createRunDefers() { // Call deferred function. b.createCall(fn.LLVMFn, forwardParams, "") - case *ssa.Extract, *ssa.Call: + case *ssa.Extract, *ssa.Call, *ssa.UnOp: expr := b.deferExprFuncs[callback] // Get the real defer struct type and cast to it. @@ -418,7 +450,41 @@ func (b *builder) createRunDefers() { // Call deferred function. b.createCall(expr.funcValueSig, forwardParams, "") + case *ssa.Builtin: + db := b.deferBuiltinFuncs[callback] + fullName := "runtime." + db.funcName + fn := b.mod.NamedFunction(fullName) + + //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. + 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) + } + + // Add the context parameter. We know it is ignored by the receiving + // function, but we have to pass one anyway. + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) + + // Parent coroutine handle. + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) + + // Call real function. + b.createCall(fn, forwardParams, "") default: panic("unknown deferred function type") } From a083d1f41cae5615db5b0d971c93ac1f1bedd47a Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Wed, 24 Jun 2020 20:11:46 -0500 Subject: [PATCH 04/15] Updated tests --- testdata/calls.go | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/testdata/calls.go b/testdata/calls.go index fd7df4915c..f01cd253c0 100644 --- a/testdata/calls.go +++ b/testdata/calls.go @@ -1,5 +1,7 @@ package main +import "time" + type Thing struct { name string } @@ -70,6 +72,10 @@ func main() { // regression testing regression1033() + + //Test deferred builtins + //this one should cause a panic + testDeferBuiltin() } func runFunc(f func(int), arg int) { @@ -116,6 +122,31 @@ func testMultiFuncVar() { defer f(1) } +func testDeferBuiltin() { + i := make(chan int) + + go func() { + j := 0 + for { + j++ + i <- j + } + }() + + go func() { + for { + select { + case n := <-i: + println(n) + } + } + }() + + time.Sleep(1000) + + defer close(i) +} + func deferred(msg string, i int) { println(msg, i) } @@ -130,11 +161,11 @@ func deferFunc() (int, func(int)) { } func multiFuncDefer() func(int) { - //i := 0 + i := 0 - /*if 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)} } From f1d1d5787cb77ea9ecf09ef71d59c97d3de4b4e2 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Wed, 24 Jun 2020 20:53:20 -0500 Subject: [PATCH 05/15] Tests should not fail --- testdata/calls.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/testdata/calls.go b/testdata/calls.go index f01cd253c0..9834278887 100644 --- a/testdata/calls.go +++ b/testdata/calls.go @@ -74,7 +74,6 @@ func main() { regression1033() //Test deferred builtins - //this one should cause a panic testDeferBuiltin() } @@ -124,26 +123,6 @@ func testMultiFuncVar() { func testDeferBuiltin() { i := make(chan int) - - go func() { - j := 0 - for { - j++ - i <- j - } - }() - - go func() { - for { - select { - case n := <-i: - println(n) - } - } - }() - - time.Sleep(1000) - defer close(i) } From 7a5c51ba7d88f156c392b86926486304688a5379 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Fri, 26 Jun 2020 12:46:23 -0500 Subject: [PATCH 06/15] Defer passes funcValue through defer frame --- compiler/compiler.go | 1 - compiler/defer.go | 27 +++++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index f437a0cbfa..caa1767c9a 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -80,7 +80,6 @@ type builder struct { } type deferExpr struct { - funcValueSig llvm.Value signature *types.Signature callback int } diff --git a/compiler/defer.go b/compiler/defer.go index 88d6611431..31a7ef8450 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -206,11 +206,9 @@ func (b *builder) createDefer(instr *ssa.Defer) { } if funcValue.IsNil() == false && sig != nil { - funcSig, context := b.decodeFuncValue(funcValue, sig) - + //funcSig, context := b.decodeFuncValue(funcValue, sig) if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok { b.deferExprFuncs[instr.Call.Value] = deferExpr{ - funcValueSig: funcSig, signature: sig, callback: len(b.allDeferFuncs), } @@ -229,8 +227,10 @@ func (b *builder) createDefer(instr *ssa.Defer) { valueTypes = append(valueTypes, llvmParam.Type()) } - values = append(values, context) - valueTypes = append(valueTypes, context.Type()) + //Pass funcValue through defer frame + values = append(values, funcValue) + valueTypes = append(valueTypes, funcValue.Type()) + } else { b.addError(instr.Pos(), "todo: defer on uncommon function call type") return @@ -432,24 +432,35 @@ func (b *builder) createRunDefers() { valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type())) } - valueTypes = append(valueTypes, b.i8ptrType) // closure + valueTypes = append(valueTypes, b.mod.GetTypeByName("runtime.funcValue")) 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 := 2; i < len(valueTypes); i++ { + i := 2 + for ; i < len(valueTypes)-1; i++ { gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") forwardParam := b.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } + //Last one is funcValue + gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") + fun := b.CreateLoad(gep, "param.func") + + //Get funcValueWithSignature and context + funcPtr, context := b.decodeFuncValue(fun, expr.signature) + + //Pass context + forwardParams = append(forwardParams, context) + // Parent coroutine handle. forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) // Call deferred function. - b.createCall(expr.funcValueSig, forwardParams, "") + b.createCall(funcPtr, forwardParams, "") case *ssa.Builtin: db := b.deferBuiltinFuncs[callback] fullName := "runtime." + db.funcName From d357ab757740e47f0642c68550d2f79abbcd22e9 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Fri, 26 Jun 2020 14:53:50 -0500 Subject: [PATCH 07/15] updated calls test --- testdata/calls.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/testdata/calls.go b/testdata/calls.go index 9834278887..9f6ba69f72 100644 --- a/testdata/calls.go +++ b/testdata/calls.go @@ -1,7 +1,5 @@ package main -import "time" - type Thing struct { name string } @@ -102,6 +100,8 @@ func testDefer() { defer t.Print("bar") println("deferring...") + d := dumb{} + defer d.Value(0) } func testDeferLoop() { @@ -126,6 +126,14 @@ func testDeferBuiltin() { defer close(i) } +type dumb struct { + +} + +func (*dumb) Value(key interface{}) interface{} { + return nil +} + func deferred(msg string, i int) { println(msg, i) } From a3b18eb6165aa376b503bd30c9fb6a4ef7b1de9e Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Fri, 26 Jun 2020 16:49:15 -0500 Subject: [PATCH 08/15] Fixed parameter offsets --- compiler/compiler.go | 5 +++-- compiler/defer.go | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index caa1767c9a..835997868c 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -80,8 +80,9 @@ type builder struct { } type deferExpr struct { - signature *types.Signature - callback int + signature *types.Signature + callback int + funcValueType llvm.Type } type deferBuiltin struct { diff --git a/compiler/defer.go b/compiler/defer.go index 31a7ef8450..b50894480e 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -209,6 +209,7 @@ func (b *builder) createDefer(instr *ssa.Defer) { //funcSig, context := b.decodeFuncValue(funcValue, sig) if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok { b.deferExprFuncs[instr.Call.Value] = deferExpr{ + funcValueType: funcValue.Type(), signature: sig, callback: len(b.allDeferFuncs), } @@ -432,22 +433,22 @@ func (b *builder) createRunDefers() { valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type())) } - valueTypes = append(valueTypes, b.mod.GetTypeByName("runtime.funcValue")) + valueTypes = append(valueTypes, expr.funcValueType) 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) - i := 2 - for ; i < len(valueTypes)-1; i++ { + funcPtrIndex := len(valueTypes)-1 + for i := 2; i < funcPtrIndex; i++ { gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") forwardParam := b.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } //Last one is funcValue - gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") + gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(funcPtrIndex), false)}, "") fun := b.CreateLoad(gep, "param.func") //Get funcValueWithSignature and context From 61161b8ce8d98ed720e5d80535606c235cfd6825 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Mon, 6 Jul 2020 23:16:37 -0500 Subject: [PATCH 09/15] Cleaned up implementation. Updated calls.txt --- compiler/compiler.go | 2 +- compiler/defer.go | 150 ++++++++++++++----------------------------- testdata/calls.txt | 4 +- 3 files changed, 53 insertions(+), 103 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index 835997868c..1ba2dab862 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -74,7 +74,7 @@ type builder struct { deferFuncs map[*ir.Function]int deferInvokeFuncs map[string]int deferClosureFuncs map[*ir.Function]int - deferExprFuncs map[interface{}]deferExpr + deferExprFuncs map[interface{}]int selectRecvBuf map[*ssa.Select]llvm.Value deferBuiltinFuncs map[interface{}]deferBuiltin } diff --git a/compiler/defer.go b/compiler/defer.go index b50894480e..617a7d9c61 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -14,7 +14,6 @@ package compiler // frames. import ( - "fmt" "github.com/tinygo-org/tinygo/compiler/llvmutil" "github.com/tinygo-org/tinygo/ir" "go/types" @@ -30,7 +29,7 @@ func (b *builder) deferInitFunc() { b.deferFuncs = make(map[*ir.Function]int) b.deferInvokeFuncs = make(map[string]int) b.deferClosureFuncs = make(map[*ir.Function]int) - b.deferExprFuncs = make(map[interface{}]deferExpr) + b.deferExprFuncs = make(map[interface{}]int) b.deferBuiltinFuncs = make(map[interface{}]deferBuiltin) // Create defer list pointer. @@ -161,7 +160,7 @@ func (b *builder) createDefer(instr *ssa.Defer) { case "close": funcName = "chanClose" default: - b.addError(instr.Pos(), fmt.Sprint("TODO: Implement defer for ", builtin.Name())) + b.addError(instr.Pos(), "todo: Implement defer for " + builtin.Name()) return } @@ -184,54 +183,27 @@ func (b *builder) createDefer(instr *ssa.Defer) { } } else { - var funcValue llvm.Value - var sig *types.Signature - - switch expr := instr.Call.Value.(type) { - case *ssa.Extract: - value := b.getValue(expr.Tuple) - funcValue = b.CreateExtractValue(value, expr.Index, "") - sig = expr.Tuple.(*ssa.Call).Call.Value.(*ssa.Function).Signature.Results().At(expr.Index).Type().Underlying().(*types.Signature) - case *ssa.Call: - funcValue = b.getValue(expr) - sig = expr.Call.Value.Type().Underlying().(*types.Signature).Results().At(0).Type().Underlying().(*types.Signature) - case *ssa.UnOp: - funcValue = b.getValue(expr) - switch ty := expr.X.Type().(type) { - case *types.Pointer: - sig = ty.Elem().Underlying().(*types.Signature) - default: - sig = ty.Underlying().(*types.Signature).Results().At(0).Type().Underlying().(*types.Signature) - } - } + funcValue := b.getValue(instr.Call.Value) + funcValueType := b.getLLVMRuntimeType("funcValue") - if funcValue.IsNil() == false && sig != nil { - //funcSig, context := b.decodeFuncValue(funcValue, sig) + if !funcValue.IsNil() && funcValue.Type() == funcValueType { if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok { - b.deferExprFuncs[instr.Call.Value] = deferExpr{ - funcValueType: funcValue.Type(), - signature: sig, - callback: len(b.allDeferFuncs), - } - b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value) + 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].callback), false) + 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} + values = []llvm.Value{callback, next, funcValue} + valueTypes = append(valueTypes, funcValueType) for _, param := range instr.Call.Args { llvmParam := b.getValue(param) values = append(values, llvmParam) valueTypes = append(valueTypes, llvmParam.Type()) } - - //Pass funcValue through defer frame - values = append(values, funcValue) - valueTypes = append(valueTypes, funcValue.Type()) - } else { b.addError(instr.Pos(), "todo: defer on uncommon function call type") return @@ -326,15 +298,26 @@ func (b *builder) createRunDefers() { switch callback := callback.(type) { case *ssa.CallCommon: // Call on an interface value. + //if !callback.IsInvoke() { + // panic("expected an invoke call, not a direct call") + //} + + // Get the real defer struct type and cast to it. + valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} + if !callback.IsInvoke() { - panic("expected an invoke call, not a direct call") + //Expect funcValue to be passed through the defer frame. + valueTypes = append(valueTypes, + b.getLLVMRuntimeType("funcValue")) + } else { + //Expect typecode + valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType) } - // 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} for _, arg := range callback.Args { valueTypes = append(valueTypes, b.getLLVMType(arg.Type())) } + deferFrameType := b.ctx.StructType(valueTypes, false) deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") @@ -347,18 +330,34 @@ func (b *builder) createRunDefers() { forwardParams = append(forwardParams, forwardParam) } - // Isolate the typecode. - typecode, forwardParams := forwardParams[0], forwardParams[1:] + var fnPtr llvm.Value - // 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)) + if !callback.IsInvoke() { + // Isolate the func value. + funcValue := forwardParams[0] + 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. forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) - fnPtr := b.getInvokePtr(callback, typecode) b.createCall(fnPtr, forwardParams, "") case *ir.Function: @@ -421,51 +420,8 @@ func (b *builder) createRunDefers() { // Call deferred function. b.createCall(fn.LLVMFn, forwardParams, "") - case *ssa.Extract, *ssa.Call, *ssa.UnOp: - expr := b.deferExprFuncs[callback] - - // Get the real defer struct type and cast to it. - valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} - - //Get signature from call results - params := expr.signature.Params() - for i := 0; i < params.Len(); i++ { - valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type())) - } - - valueTypes = append(valueTypes, expr.funcValueType) - 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) - funcPtrIndex := len(valueTypes)-1 - for i := 2; i < funcPtrIndex; i++ { - gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") - forwardParam := b.CreateLoad(gep, "param") - forwardParams = append(forwardParams, forwardParam) - } - - //Last one is funcValue - gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(funcPtrIndex), false)}, "") - fun := b.CreateLoad(gep, "param.func") - - //Get funcValueWithSignature and context - funcPtr, context := b.decodeFuncValue(fun, expr.signature) - - //Pass context - forwardParams = append(forwardParams, context) - - // Parent coroutine handle. - forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) - - // Call deferred function. - b.createCall(funcPtr, forwardParams, "") case *ssa.Builtin: db := b.deferBuiltinFuncs[callback] - fullName := "runtime." + db.funcName - fn := b.mod.NamedFunction(fullName) //Get parameter types valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} @@ -480,7 +436,7 @@ func (b *builder) createRunDefers() { deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") // Extract the params from the struct. - forwardParams := []llvm.Value{} + 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") @@ -488,15 +444,7 @@ func (b *builder) createRunDefers() { forwardParams = append(forwardParams, forwardParam) } - // Add the context parameter. We know it is ignored by the receiving - // function, but we have to pass one anyway. - forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) - - // Parent coroutine handle. - forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) - - // Call real function. - b.createCall(fn, forwardParams, "") + b.createRuntimeCall(db.funcName, forwardParams, "") default: panic("unknown deferred function type") } diff --git a/testdata/calls.txt b/testdata/calls.txt index b65e7d9a40..c3f59e17a5 100644 --- a/testdata/calls.txt +++ b/testdata/calls.txt @@ -9,8 +9,10 @@ loop 3 loop 2 loop 1 loop 0 +...extracted defer func 1 +Called the correct function. i = 1 bound method: foo thing inside closure: foo inside fp closure: foo 3 Thing.Print: arg: functional args 1 -Thing.Print: named thing arg: functional args 2 +Thing.Print: named thing arg: functional args 2 \ No newline at end of file From 102e58d3f4028281b1e61d527ebd11c6506dba5a Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Tue, 7 Jul 2020 08:29:34 -0500 Subject: [PATCH 10/15] removed inspection of func value --- compiler/defer.go | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/compiler/defer.go b/compiler/defer.go index 617a7d9c61..72e863bfb1 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -184,29 +184,23 @@ func (b *builder) createDefer(instr *ssa.Defer) { } else { funcValue := b.getValue(instr.Call.Value) - funcValueType := b.getLLVMRuntimeType("funcValue") + + if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok { + b.deferExprFuncs[instr.Call.Value] = len(b.allDeferFuncs) + b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call) + } - if !funcValue.IsNil() && funcValue.Type() == funcValueType { - 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) - 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, funcValueType) - for _, param := range instr.Call.Args { - llvmParam := b.getValue(param) - values = append(values, llvmParam) - valueTypes = append(valueTypes, llvmParam.Type()) - } - } else { - b.addError(instr.Pos(), "todo: defer on uncommon function call type") - return + // 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()) } } From 86c529f48a2c080749d3df460ab3b3c75c001291 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Tue, 7 Jul 2020 10:33:40 -0500 Subject: [PATCH 11/15] Assumed incorrect llvm type for funcValue. Uses b.getFucType instead. --- compiler/compiler.go | 6 ------ compiler/defer.go | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index 1ba2dab862..a0b922bd1b 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -79,12 +79,6 @@ type builder struct { deferBuiltinFuncs map[interface{}]deferBuiltin } -type deferExpr struct { - signature *types.Signature - callback int - funcValueType llvm.Type -} - type deferBuiltin struct { funcName string callback int diff --git a/compiler/defer.go b/compiler/defer.go index 72e863bfb1..8f2695b17a 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -301,8 +301,7 @@ func (b *builder) createRunDefers() { if !callback.IsInvoke() { //Expect funcValue to be passed through the defer frame. - valueTypes = append(valueTypes, - b.getLLVMRuntimeType("funcValue")) + valueTypes = append(valueTypes, b.getFuncType(callback.Signature())) } else { //Expect typecode valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType) From 7a14effd595118dc9a43dc697a8a9b5b0c640f11 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Tue, 7 Jul 2020 11:50:34 -0500 Subject: [PATCH 12/15] Needed empty line at end of calls.txt??? --- testdata/calls.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/calls.txt b/testdata/calls.txt index c3f59e17a5..78c07caf4d 100644 --- a/testdata/calls.txt +++ b/testdata/calls.txt @@ -15,4 +15,4 @@ bound method: foo thing inside closure: foo inside fp closure: foo 3 Thing.Print: arg: functional args 1 -Thing.Print: named thing arg: functional args 2 \ No newline at end of file +Thing.Print: named thing arg: functional args 2 From fd8f7477805fac81f050a612a0bdb10a120bfcd1 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Tue, 7 Jul 2020 12:17:45 -0500 Subject: [PATCH 13/15] formatted code --- compiler/defer.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/defer.go b/compiler/defer.go index 8f2695b17a..4e220fa854 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -154,18 +154,18 @@ func (b *builder) createDefer(instr *ssa.Defer) { values = append(values, context) valueTypes = append(valueTypes, context.Type()) - } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { + } 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()) + 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 { + b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{ funcName, len(b.allDeferFuncs), } @@ -184,7 +184,7 @@ func (b *builder) createDefer(instr *ssa.Defer) { } else { funcValue := b.getValue(instr.Call.Value) - + if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok { b.deferExprFuncs[instr.Call.Value] = len(b.allDeferFuncs) b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call) From b530cd601aa93c7f25a12efa1e01b4ed9c9efd31 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Tue, 7 Jul 2020 16:41:50 -0500 Subject: [PATCH 14/15] removed commented out code --- compiler/defer.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/defer.go b/compiler/defer.go index 4e220fa854..fa5b979979 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -291,10 +291,7 @@ func (b *builder) createRunDefers() { b.SetInsertPointAtEnd(block) switch callback := callback.(type) { case *ssa.CallCommon: - // Call on an interface value. - //if !callback.IsInvoke() { - // panic("expected an invoke call, not a direct call") - //} + // Call on an value or interface value. // Get the real defer struct type and cast to it. valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} From 1c99bc5f68b5709e86e6a6e23c14739143ea4920 Mon Sep 17 00:00:00 2001 From: "Justin A. Wilson" Date: Mon, 13 Jul 2020 23:34:07 -0500 Subject: [PATCH 15/15] changed map key type to ssa.Value. --- compiler/compiler.go | 4 ++-- compiler/defer.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index a0b922bd1b..b9a61d8fac 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -74,9 +74,9 @@ type builder struct { deferFuncs map[*ir.Function]int deferInvokeFuncs map[string]int deferClosureFuncs map[*ir.Function]int - deferExprFuncs map[interface{}]int + deferExprFuncs map[ssa.Value]int selectRecvBuf map[*ssa.Select]llvm.Value - deferBuiltinFuncs map[interface{}]deferBuiltin + deferBuiltinFuncs map[ssa.Value]deferBuiltin } type deferBuiltin struct { diff --git a/compiler/defer.go b/compiler/defer.go index fa5b979979..db2bed3282 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -29,8 +29,8 @@ func (b *builder) deferInitFunc() { b.deferFuncs = make(map[*ir.Function]int) b.deferInvokeFuncs = make(map[string]int) b.deferClosureFuncs = make(map[*ir.Function]int) - b.deferExprFuncs = make(map[interface{}]int) - b.deferBuiltinFuncs = make(map[interface{}]deferBuiltin) + b.deferExprFuncs = make(map[ssa.Value]int) + b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin) // Create defer list pointer. deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)