Merge pull request #1011 from visualfc/deferargs2

support defer args
This commit is contained in:
xushiwei
2025-02-28 18:14:16 +08:00
committed by GitHub
3 changed files with 89 additions and 2 deletions

18
_demo/defer/main.go Normal file
View File

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

View File

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

View File

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