diff --git a/_demo/defer/main.go b/_demo/defer/main.go new file mode 100644 index 00000000..d46620a9 --- /dev/null +++ b/_demo/defer/main.go @@ -0,0 +1,18 @@ +package main + +func main() { + var a int = 5 + defer println(a) + defer func() { + println(a) + }() + defer func() { + println(recover().(string)) + }() + a = 10 + panic("error") + //Output: + // error + // 10 + // 5 +} diff --git a/compiler/ssa/eh.go b/compiler/ssa/eh.go index 2e30e867..7bb3a568 100644 --- a/compiler/ssa/eh.go +++ b/compiler/ssa/eh.go @@ -97,6 +97,7 @@ type aDefer struct { bitsPtr Expr // pointer to defer bits rethPtr Expr // next block of Rethrow rundPtr Expr // next block of RunDefers + argsPtr Expr // func and args links procBlk BasicBlock // deferProc block panicBlk BasicBlock // panic block (runDefers and rethrow) rundsNext []BasicBlock // next blocks of RunDefers @@ -133,11 +134,13 @@ const ( // 2: link *Defer // 3: reth voidptr: block address after Rethrow // 4: rund voidptr: block address after RunDefers + // 5: func and args links deferSigjmpbuf = iota deferBits deferLink deferRethrow deferRunDefers + deferArgs ) func (b Builder) getDefer(kind DoAction) *aDefer { @@ -167,6 +170,7 @@ func (b Builder) getDefer(kind DoAction) *aDefer { bitsPtr := b.FieldAddr(deferData, deferBits) rethPtr := b.FieldAddr(deferData, deferRethrow) rundPtr := b.FieldAddr(deferData, deferRunDefers) + argsPtr := b.FieldAddr(deferData, deferArgs) czero := prog.IntVal(0, prog.CInt()) retval := b.Sigsetjmp(jb, czero) @@ -184,6 +188,7 @@ func (b Builder) getDefer(kind DoAction) *aDefer { bitsPtr: bitsPtr, rethPtr: rethPtr, rundPtr: rundPtr, + argsPtr: argsPtr, procBlk: procBlk, panicBlk: panicBlk, rundsNext: []BasicBlock{rethrowBlk}, @@ -228,20 +233,83 @@ func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) { default: panic("todo: DeferInLoop is not supported - " + b.Func.Name()) } + typ := b.saveDeferArgs(self, fn, args) self.stmts = append(self.stmts, func(bits Expr) { switch kind { case DeferInCond: zero := prog.Val(uintptr(0)) has := b.BinOp(token.NEQ, b.BinOp(token.AND, bits, nextbit), zero) b.IfThen(has, func() { - b.Call(fn, args...) + b.callDefer(self, typ, fn, args) }) case DeferAlways: - b.Call(fn, args...) + b.callDefer(self, typ, fn, args) } }) } +/* +type node struct { + prev *node + fn func() + args ... +} +// push +defer.Args = &node{defer.Args,fn,args...} +// pop +node := defer.Args +defer.Args = node.prev +free(node) +*/ + +func (b Builder) saveDeferArgs(self *aDefer, fn Expr, args []Expr) Type { + if fn.kind != vkClosure && len(args) == 0 { + return nil + } + prog := b.Prog + offset := 1 + if fn.kind == vkClosure { + offset++ + } + typs := make([]Type, len(args)+offset) + flds := make([]llvm.Value, len(args)+offset) + typs[0] = prog.VoidPtr() + flds[0] = b.Load(self.argsPtr).impl + if offset == 2 { + typs[1] = fn.Type + flds[1] = fn.impl + } + for i, arg := range args { + typs[i+offset] = arg.Type + flds[i+offset] = arg.impl + } + typ := prog.Struct(typs...) + ptr := Expr{b.aggregateMalloc(typ, flds...), prog.VoidPtr()} + b.Store(self.argsPtr, ptr) + return typ +} + +func (b Builder) callDefer(self *aDefer, typ Type, fn Expr, args []Expr) { + if typ == nil { + b.Call(fn, args...) + return + } + prog := b.Prog + ptr := b.Load(self.argsPtr) + data := b.Load(Expr{ptr.impl, prog.Pointer(typ)}) + offset := 1 + b.Store(self.argsPtr, Expr{b.getField(data, 0).impl, prog.VoidPtr()}) + if fn.kind == vkClosure { + fn = b.getField(data, 1) + offset++ + } + for i := 0; i < len(args); i++ { + args[i] = b.getField(data, i+offset) + } + b.Call(fn, args...) + b.free(ptr) +} + // RunDefers emits instructions to run deferred instructions. func (b Builder) RunDefers() { self := b.getDefer(DeferInCond) diff --git a/runtime/internal/runtime/z_rt.go b/runtime/internal/runtime/z_rt.go index 2b277c7b..ba31b2c6 100644 --- a/runtime/internal/runtime/z_rt.go +++ b/runtime/internal/runtime/z_rt.go @@ -35,6 +35,7 @@ type Defer struct { Link *Defer Reth unsafe.Pointer // block address after Rethrow Rund unsafe.Pointer // block address after RunDefers + Args unsafe.Pointer // defer func and args links } // Recover recovers a panic.