From 60dd33b48fac4c71e1ccfaf4c3042bdd8d015f42 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 9 Jun 2024 09:08:22 +0800 Subject: [PATCH] llgo/ssa: defer support panic; IndirectJump/Switch --- internal/runtime/z_rt.go | 1 + ssa/eh.go | 92 ++++++++++++++++++++++++++-------------- ssa/python.go | 3 +- ssa/stmt_builder.go | 69 +++++++++++++++++++++++++++++- 4 files changed, 131 insertions(+), 34 deletions(-) diff --git a/internal/runtime/z_rt.go b/internal/runtime/z_rt.go index 4d178efe..3b5b29b7 100644 --- a/internal/runtime/z_rt.go +++ b/internal/runtime/z_rt.go @@ -27,6 +27,7 @@ import ( // Defer presents defer statements in a function. type Defer struct { + Addr unsafe.Pointer Bits uintptr Link *Defer Rund int // index of RunDefers diff --git a/ssa/eh.go b/ssa/eh.go index 684274f1..0dac60e8 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -68,8 +68,9 @@ func (b Builder) Sigsetjmp(jb, savemask Expr) Expr { } func (b Builder) Siglongjmp(jb, retval Expr) { - fn := b.Pkg.cFunc("siglongjmp", b.Prog.tySiglongjmp()) + fn := b.Pkg.cFunc("siglongjmp", b.Prog.tySiglongjmp()) // TODO(xsw): mark as noreturn b.Call(fn, jb, retval) + b.Unreachable() } // ----------------------------------------------------------------------------- @@ -78,21 +79,21 @@ const ( deferKey = "__llgo_defer" ) -func (p Function) deferInitBuilder() Builder { - b := p.NewBuilder() - b.SetBlockEx(p.blks[0], BeforeLast, true) - return b +func (p Function) deferInitBuilder() (b Builder, next BasicBlock) { + b = p.NewBuilder() + next = b.setBlockMoveLast(p.blks[0]) + return } type aDefer struct { - nextBit int // next defer bit - key Expr // pthread TLS key - data Expr // pointer to runtime.Defer - bitsPtr Expr // pointer to defer bits - rundPtr Expr // pointer to RunDefers index - procBlk BasicBlock // deferProc block - stmts []func(bits Expr) + nextBit int // next defer bit + key Expr // pthread TLS key + data Expr // pointer to runtime.Defer + bitsPtr Expr // pointer to defer bits + rundPtr Expr // pointer to RunDefers index + procBlk BasicBlock // deferProc block runsNext []BasicBlock // next blocks of RunDefers + stmts []func(bits Expr) } func (p Package) deferInit() { @@ -123,25 +124,59 @@ func (b Builder) deferKey() Expr { func (b Builder) getDefer(kind DoAction) *aDefer { self := b.Func if self.defer_ == nil { - // 0: bits uintptr - // 1: link *Defer - // 2: rund int + // TODO(xsw): check if in pkg.init + // 0: addr sigjmpbuf + // 1: bits uintptr + // 2: link *Defer + // 3: rund int + const ( + deferSigjmpbuf = iota + deferBits + deferLink + deferRund + ) + var next, rundBlk BasicBlock if kind != DeferAlways { - b = self.deferInitBuilder() + b, next = self.deferInitBuilder() } prog := b.Prog key := b.deferKey() zero := prog.Val(uintptr(0)) - link := b.pthreadGetspecific(key) - ptr := b.aggregateAlloca(prog.Defer(), zero.impl, link.impl) + link := Expr{b.pthreadGetspecific(key).impl, prog.DeferPtr()} + jb := b.AllocaSigjmpBuf() + ptr := b.aggregateAlloca(prog.Defer(), jb.impl, zero.impl, link.impl) deferData := Expr{ptr, prog.DeferPtr()} b.pthreadSetspecific(key, deferData) + blks := self.MakeBlocks(2) + procBlk, throwBlk := blks[0], blks[1] + bitsPtr := b.FieldAddr(deferData, deferBits) + rundPtr := b.FieldAddr(deferData, deferRund) self.defer_ = &aDefer{ - key: key, - data: deferData, - bitsPtr: b.FieldAddr(deferData, 0), - rundPtr: b.FieldAddr(deferData, 2), - procBlk: self.MakeBlock(), + key: key, + data: deferData, + bitsPtr: bitsPtr, + rundPtr: rundPtr, + procBlk: procBlk, + runsNext: []BasicBlock{throwBlk}, + } + czero := prog.IntVal(0, prog.CInt()) + retval := b.Sigsetjmp(jb, czero) + if kind != DeferAlways { + rundBlk = self.MakeBlock() + } else { + blks = self.MakeBlocks(2) + next, rundBlk = blks[0], blks[1] + } + b.If(b.BinOp(token.EQL, retval, czero), next, rundBlk) + b.SetBlockEx(rundBlk, AtEnd, false) // exec runDefers and throw + b.Store(rundPtr, prog.Val(0)) + b.Jump(procBlk) + b.SetBlockEx(throwBlk, AtEnd, false) // throw + linkJBPtr := b.FieldAddr(link, deferSigjmpbuf) + b.Siglongjmp(b.Load(linkJBPtr), prog.IntVal(1, prog.CInt())) + if kind == DeferAlways { + b.SetBlockEx(next, AtEnd, false) + b.blk.last = next.last } } return self.defer_ @@ -202,8 +237,7 @@ func (p Function) endDefer(b Builder) { return } nexts := self.runsNext - n := len(nexts) - if n == 0 { + if len(nexts) == 0 { return } b.SetBlockEx(self.procBlk, AtEnd, true) @@ -216,12 +250,8 @@ func (p Function) endDefer(b Builder) { link := b.getField(b.Load(self.data), 2) b.pthreadSetspecific(self.key, link) - prog := b.Prog - rund := b.Load(self.rundPtr) - sw := b.impl.CreateSwitch(rund.impl, nexts[0].first, n-1) - for i := 1; i < n; i++ { - sw.AddCase(prog.Val(i).impl, nexts[i].first) - } + // rund := b.Load(self.rundPtr) + b.IndirectJump(self.rundPtr, nexts) } // ----------------------------------------------------------------------------- diff --git a/ssa/python.go b/ssa/python.go index 1c8a7bf3..2ff344a4 100644 --- a/ssa/python.go +++ b/ssa/python.go @@ -200,7 +200,8 @@ func (p Program) tyGetAttrString() *types.Signature { func (p Package) PyInit() bool { if fn := p.FuncOf("main"); fn != nil { b := fn.NewBuilder() - b.SetBlockEx(fn.Block(0), AtStart, false).callPyInit() + b.SetBlockEx(fn.Block(0), AtStart, false) + b.callPyInit() b.Dispose() return true } diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index 40b58041..e5220946 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -80,6 +80,21 @@ func (b Builder) SetBlock(blk BasicBlock) Builder { return b } +func (b Builder) setBlockMoveLast(blk BasicBlock) (next BasicBlock) { + blkLast := blk.last + last := blkLast.LastInstruction() + last.RemoveFromParentAsInstruction() + + impl := b.impl + + next = b.Func.MakeBlock() + impl.SetInsertPointAtEnd(next.last) + impl.Insert(last) + + impl.SetInsertPointAtEnd(blkLast) + return +} + type InsertPoint int const ( @@ -90,7 +105,7 @@ const ( ) // SetBlockEx sets blk as current basic block and pos as its insert point. -func (b Builder) SetBlockEx(blk BasicBlock, pos InsertPoint, setBlk bool) Builder { +func (b Builder) SetBlockEx(blk BasicBlock, pos InsertPoint, setBlk bool) { if b.Func != blk.fn { panic("mismatched function") } @@ -109,7 +124,6 @@ func (b Builder) SetBlockEx(blk BasicBlock, pos InsertPoint, setBlk bool) Builde if setBlk { b.blk = blk } - return b } func instrAfterInit(blk llvm.BasicBlock) llvm.Value { @@ -188,6 +202,17 @@ func (b Builder) Jump(jmpb BasicBlock) { b.impl.CreateBr(jmpb.first) } +// IndirectJump emits an indirect jump instruction. +func (b Builder) IndirectJump(addr Expr, dests []BasicBlock) { + if debugInstr { + log.Printf("IndirectJump %v\n", addr.impl) + } + ibr := b.impl.CreateIndirectBr(addr.impl, len(dests)) + for _, dest := range dests { + ibr.AddDest(dest.first) + } +} + // If emits an if instruction. func (b Builder) If(cond Expr, thenb, elseb BasicBlock) { if b.Func != thenb.fn || b.Func != elseb.fn { @@ -210,6 +235,46 @@ func (b Builder) IfThen(cond Expr, then func()) { b.blk.last = blks[1].last } +// ----------------------------------------------------------------------------- +/* +type caseStmt struct { + v llvm.Value + blk llvm.BasicBlock +} + +type aSwitch struct { + v llvm.Value + def llvm.BasicBlock + cases []caseStmt +} + +// Switch represents a switch statement. +type Switch = *aSwitch + +// Case emits a case instruction. +func (p Switch) Case(v Expr, blk BasicBlock) { + if debugInstr { + log.Printf("Case %v, _llgo_%v\n", v.impl, blk.idx) + } + p.cases = append(p.cases, caseStmt{v.impl, blk.first}) +} + +// End ends a switch statement. +func (p Switch) End(b Builder) { + sw := b.impl.CreateSwitch(p.v, p.def, len(p.cases)) + for _, c := range p.cases { + sw.AddCase(c.v, c.blk) + } +} + +// Switch starts a switch statement. +func (b Builder) Switch(v Expr, defb BasicBlock) Switch { + if debugInstr { + log.Printf("Switch %v, _llgo_%v\n", v.impl, defb.idx) + } + return &aSwitch{v.impl, defb.first, nil} +} +*/ // ----------------------------------------------------------------------------- // Phi represents a phi node.