Skip to content

Commit 848c3e5

Browse files
waj334justin12343
andauthored
compiler: implement func value and builtin defers
Co-authored-by: Justin A. Wilson <[email protected]>
1 parent 3650c2c commit 848c3e5

File tree

4 files changed

+166
-13
lines changed

4 files changed

+166
-13
lines changed

compiler/compiler.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,14 @@ type builder struct {
7474
deferFuncs map[*ir.Function]int
7575
deferInvokeFuncs map[string]int
7676
deferClosureFuncs map[*ir.Function]int
77+
deferExprFuncs map[ssa.Value]int
7778
selectRecvBuf map[*ssa.Select]llvm.Value
79+
deferBuiltinFuncs map[ssa.Value]deferBuiltin
80+
}
81+
82+
type deferBuiltin struct {
83+
funcName string
84+
callback int
7885
}
7986

8087
type phiNode struct {

compiler/defer.go

Lines changed: 108 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package compiler
1616
import (
1717
"github.com/tinygo-org/tinygo/compiler/llvmutil"
1818
"github.com/tinygo-org/tinygo/ir"
19+
"go/types"
1920
"golang.org/x/tools/go/ssa"
2021
"tinygo.org/x/go-llvm"
2122
)
@@ -28,6 +29,8 @@ func (b *builder) deferInitFunc() {
2829
b.deferFuncs = make(map[*ir.Function]int)
2930
b.deferInvokeFuncs = make(map[string]int)
3031
b.deferClosureFuncs = make(map[*ir.Function]int)
32+
b.deferExprFuncs = make(map[ssa.Value]int)
33+
b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
3134

3235
// Create defer list pointer.
3336
deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)
@@ -151,9 +154,54 @@ func (b *builder) createDefer(instr *ssa.Defer) {
151154
values = append(values, context)
152155
valueTypes = append(valueTypes, context.Type())
153156

157+
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
158+
var funcName string
159+
switch builtin.Name() {
160+
case "close":
161+
funcName = "chanClose"
162+
default:
163+
b.addError(instr.Pos(), "todo: Implement defer for "+builtin.Name())
164+
return
165+
}
166+
167+
if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
168+
b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
169+
funcName,
170+
len(b.allDeferFuncs),
171+
}
172+
b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value)
173+
}
174+
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferBuiltinFuncs[instr.Call.Value].callback), false)
175+
176+
// Collect all values to be put in the struct (starting with
177+
// runtime._defer fields).
178+
values = []llvm.Value{callback, next}
179+
for _, param := range instr.Call.Args {
180+
llvmParam := b.getValue(param)
181+
values = append(values, llvmParam)
182+
valueTypes = append(valueTypes, llvmParam.Type())
183+
}
184+
154185
} else {
155-
b.addError(instr.Pos(), "todo: defer on uncommon function call type")
156-
return
186+
funcValue := b.getValue(instr.Call.Value)
187+
188+
if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok {
189+
b.deferExprFuncs[instr.Call.Value] = len(b.allDeferFuncs)
190+
b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call)
191+
}
192+
193+
callback := llvm.ConstInt(b.uintptrType, uint64(b.deferExprFuncs[instr.Call.Value]), false)
194+
195+
// Collect all values to be put in the struct (starting with
196+
// runtime._defer fields, followed by all parameters including the
197+
// context pointer).
198+
values = []llvm.Value{callback, next, funcValue}
199+
valueTypes = append(valueTypes, funcValue.Type())
200+
for _, param := range instr.Call.Args {
201+
llvmParam := b.getValue(param)
202+
values = append(values, llvmParam)
203+
valueTypes = append(valueTypes, llvmParam.Type())
204+
}
157205
}
158206

159207
// Make a struct out of the collected values to put in the defer frame.
@@ -243,16 +291,23 @@ func (b *builder) createRunDefers() {
243291
b.SetInsertPointAtEnd(block)
244292
switch callback := callback.(type) {
245293
case *ssa.CallCommon:
246-
// Call on an interface value.
294+
// Call on an value or interface value.
295+
296+
// Get the real defer struct type and cast to it.
297+
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
298+
247299
if !callback.IsInvoke() {
248-
panic("expected an invoke call, not a direct call")
300+
//Expect funcValue to be passed through the defer frame.
301+
valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
302+
} else {
303+
//Expect typecode
304+
valueTypes = append(valueTypes, b.uintptrType, b.i8ptrType)
249305
}
250306

251-
// Get the real defer struct type and cast to it.
252-
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0), b.uintptrType, b.i8ptrType}
253307
for _, arg := range callback.Args {
254308
valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
255309
}
310+
256311
deferFrameType := b.ctx.StructType(valueTypes, false)
257312
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
258313

@@ -265,18 +320,34 @@ func (b *builder) createRunDefers() {
265320
forwardParams = append(forwardParams, forwardParam)
266321
}
267322

268-
// Isolate the typecode.
269-
typecode, forwardParams := forwardParams[0], forwardParams[1:]
323+
var fnPtr llvm.Value
270324

271-
// Add the context parameter. An interface call cannot also be a
272-
// closure but we have to supply the parameter anyway for platforms
273-
// with a strict calling convention.
274-
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
325+
if !callback.IsInvoke() {
326+
// Isolate the func value.
327+
funcValue := forwardParams[0]
328+
forwardParams = forwardParams[1:]
329+
330+
//Get function pointer and context
331+
fp, context := b.decodeFuncValue(funcValue, callback.Signature())
332+
fnPtr = fp
333+
334+
//Pass context
335+
forwardParams = append(forwardParams, context)
336+
} else {
337+
// Isolate the typecode.
338+
typecode := forwardParams[0]
339+
forwardParams = forwardParams[1:]
340+
fnPtr = b.getInvokePtr(callback, typecode)
341+
342+
// Add the context parameter. An interface call cannot also be a
343+
// closure but we have to supply the parameter anyway for platforms
344+
// with a strict calling convention.
345+
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
346+
}
275347

276348
// Parent coroutine handle.
277349
forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType))
278350

279-
fnPtr := b.getInvokePtr(callback, typecode)
280351
b.createCall(fnPtr, forwardParams, "")
281352

282353
case *ir.Function:
@@ -339,7 +410,31 @@ func (b *builder) createRunDefers() {
339410

340411
// Call deferred function.
341412
b.createCall(fn.LLVMFn, forwardParams, "")
413+
case *ssa.Builtin:
414+
db := b.deferBuiltinFuncs[callback]
415+
416+
//Get parameter types
417+
valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)}
418+
419+
//Get signature from call results
420+
params := callback.Type().Underlying().(*types.Signature).Params()
421+
for i := 0; i < params.Len(); i++ {
422+
valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
423+
}
424+
425+
deferFrameType := b.ctx.StructType(valueTypes, false)
426+
deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame")
427+
428+
// Extract the params from the struct.
429+
var forwardParams []llvm.Value
430+
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
431+
for i := 0; i < params.Len(); i++ {
432+
gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
433+
forwardParam := b.CreateLoad(gep, "param")
434+
forwardParams = append(forwardParams, forwardParam)
435+
}
342436

437+
b.createRuntimeCall(db.funcName, forwardParams, "")
343438
default:
344439
panic("unknown deferred function type")
345440
}

testdata/calls.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ func main() {
4444
// defers in loop
4545
testDeferLoop()
4646

47+
//defer func variable call
48+
testDeferFuncVar()
49+
50+
//More complicated func variable call
51+
testMultiFuncVar()
52+
4753
// Take a bound method and use it as a function pointer.
4854
// This function pointer needs a context pointer.
4955
testBound(thing.String)
@@ -64,6 +70,9 @@ func main() {
6470

6571
// regression testing
6672
regression1033()
73+
74+
//Test deferred builtins
75+
testDeferBuiltin()
6776
}
6877

6978
func runFunc(f func(int), arg int) {
@@ -91,6 +100,8 @@ func testDefer() {
91100
defer t.Print("bar")
92101

93102
println("deferring...")
103+
d := dumb{}
104+
defer d.Value(0)
94105
}
95106

96107
func testDeferLoop() {
@@ -99,6 +110,30 @@ func testDeferLoop() {
99110
}
100111
}
101112

113+
func testDeferFuncVar() {
114+
dummy, f := deferFunc()
115+
dummy++
116+
defer f(1)
117+
}
118+
119+
func testMultiFuncVar() {
120+
f := multiFuncDefer()
121+
defer f(1)
122+
}
123+
124+
func testDeferBuiltin() {
125+
i := make(chan int)
126+
defer close(i)
127+
}
128+
129+
type dumb struct {
130+
131+
}
132+
133+
func (*dumb) Value(key interface{}) interface{} {
134+
return nil
135+
}
136+
102137
func deferred(msg string, i int) {
103138
println(msg, i)
104139
}
@@ -108,6 +143,20 @@ func exportedDefer() {
108143
println("...exported defer")
109144
}
110145

146+
func deferFunc() (int, func(int)) {
147+
return 0, func(i int){println("...extracted defer func ", i)}
148+
}
149+
150+
func multiFuncDefer() func(int) {
151+
i := 0
152+
153+
if i > 0 {
154+
return func(i int){println("Should not have gotten here. i = ", i)}
155+
}
156+
157+
return func(i int){println("Called the correct function. i = ", i)}
158+
}
159+
111160
func testBound(f func() string) {
112161
println("bound method:", f())
113162
}

testdata/calls.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ loop 3
99
loop 2
1010
loop 1
1111
loop 0
12+
...extracted defer func 1
13+
Called the correct function. i = 1
1214
bound method: foo
1315
thing inside closure: foo
1416
inside fp closure: foo 3

0 commit comments

Comments
 (0)