llgo/ssa: defer support panic; IndirectJump/Switch

This commit is contained in:
xushiwei
2024-06-09 09:08:22 +08:00
parent 8b7d8b7786
commit 60dd33b48f
4 changed files with 131 additions and 34 deletions

View File

@@ -27,6 +27,7 @@ import (
// Defer presents defer statements in a function. // Defer presents defer statements in a function.
type Defer struct { type Defer struct {
Addr unsafe.Pointer
Bits uintptr Bits uintptr
Link *Defer Link *Defer
Rund int // index of RunDefers Rund int // index of RunDefers

View File

@@ -68,8 +68,9 @@ func (b Builder) Sigsetjmp(jb, savemask Expr) Expr {
} }
func (b Builder) Siglongjmp(jb, retval 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.Call(fn, jb, retval)
b.Unreachable()
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -78,10 +79,10 @@ const (
deferKey = "__llgo_defer" deferKey = "__llgo_defer"
) )
func (p Function) deferInitBuilder() Builder { func (p Function) deferInitBuilder() (b Builder, next BasicBlock) {
b := p.NewBuilder() b = p.NewBuilder()
b.SetBlockEx(p.blks[0], BeforeLast, true) next = b.setBlockMoveLast(p.blks[0])
return b return
} }
type aDefer struct { type aDefer struct {
@@ -91,8 +92,8 @@ type aDefer struct {
bitsPtr Expr // pointer to defer bits bitsPtr Expr // pointer to defer bits
rundPtr Expr // pointer to RunDefers index rundPtr Expr // pointer to RunDefers index
procBlk BasicBlock // deferProc block procBlk BasicBlock // deferProc block
stmts []func(bits Expr)
runsNext []BasicBlock // next blocks of RunDefers runsNext []BasicBlock // next blocks of RunDefers
stmts []func(bits Expr)
} }
func (p Package) deferInit() { func (p Package) deferInit() {
@@ -123,25 +124,59 @@ func (b Builder) deferKey() Expr {
func (b Builder) getDefer(kind DoAction) *aDefer { func (b Builder) getDefer(kind DoAction) *aDefer {
self := b.Func self := b.Func
if self.defer_ == nil { if self.defer_ == nil {
// 0: bits uintptr // TODO(xsw): check if in pkg.init
// 1: link *Defer // 0: addr sigjmpbuf
// 2: rund int // 1: bits uintptr
// 2: link *Defer
// 3: rund int
const (
deferSigjmpbuf = iota
deferBits
deferLink
deferRund
)
var next, rundBlk BasicBlock
if kind != DeferAlways { if kind != DeferAlways {
b = self.deferInitBuilder() b, next = self.deferInitBuilder()
} }
prog := b.Prog prog := b.Prog
key := b.deferKey() key := b.deferKey()
zero := prog.Val(uintptr(0)) zero := prog.Val(uintptr(0))
link := b.pthreadGetspecific(key) link := Expr{b.pthreadGetspecific(key).impl, prog.DeferPtr()}
ptr := b.aggregateAlloca(prog.Defer(), zero.impl, link.impl) jb := b.AllocaSigjmpBuf()
ptr := b.aggregateAlloca(prog.Defer(), jb.impl, zero.impl, link.impl)
deferData := Expr{ptr, prog.DeferPtr()} deferData := Expr{ptr, prog.DeferPtr()}
b.pthreadSetspecific(key, deferData) 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{ self.defer_ = &aDefer{
key: key, key: key,
data: deferData, data: deferData,
bitsPtr: b.FieldAddr(deferData, 0), bitsPtr: bitsPtr,
rundPtr: b.FieldAddr(deferData, 2), rundPtr: rundPtr,
procBlk: self.MakeBlock(), 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_ return self.defer_
@@ -202,8 +237,7 @@ func (p Function) endDefer(b Builder) {
return return
} }
nexts := self.runsNext nexts := self.runsNext
n := len(nexts) if len(nexts) == 0 {
if n == 0 {
return return
} }
b.SetBlockEx(self.procBlk, AtEnd, true) b.SetBlockEx(self.procBlk, AtEnd, true)
@@ -216,12 +250,8 @@ func (p Function) endDefer(b Builder) {
link := b.getField(b.Load(self.data), 2) link := b.getField(b.Load(self.data), 2)
b.pthreadSetspecific(self.key, link) b.pthreadSetspecific(self.key, link)
prog := b.Prog // rund := b.Load(self.rundPtr)
rund := b.Load(self.rundPtr) b.IndirectJump(self.rundPtr, nexts)
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)
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -200,7 +200,8 @@ func (p Program) tyGetAttrString() *types.Signature {
func (p Package) PyInit() bool { func (p Package) PyInit() bool {
if fn := p.FuncOf("main"); fn != nil { if fn := p.FuncOf("main"); fn != nil {
b := fn.NewBuilder() b := fn.NewBuilder()
b.SetBlockEx(fn.Block(0), AtStart, false).callPyInit() b.SetBlockEx(fn.Block(0), AtStart, false)
b.callPyInit()
b.Dispose() b.Dispose()
return true return true
} }

View File

@@ -80,6 +80,21 @@ func (b Builder) SetBlock(blk BasicBlock) Builder {
return b 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 type InsertPoint int
const ( const (
@@ -90,7 +105,7 @@ const (
) )
// SetBlockEx sets blk as current basic block and pos as its insert point. // 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 { if b.Func != blk.fn {
panic("mismatched function") panic("mismatched function")
} }
@@ -109,7 +124,6 @@ func (b Builder) SetBlockEx(blk BasicBlock, pos InsertPoint, setBlk bool) Builde
if setBlk { if setBlk {
b.blk = blk b.blk = blk
} }
return b
} }
func instrAfterInit(blk llvm.BasicBlock) llvm.Value { func instrAfterInit(blk llvm.BasicBlock) llvm.Value {
@@ -188,6 +202,17 @@ func (b Builder) Jump(jmpb BasicBlock) {
b.impl.CreateBr(jmpb.first) 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. // If emits an if instruction.
func (b Builder) If(cond Expr, thenb, elseb BasicBlock) { func (b Builder) If(cond Expr, thenb, elseb BasicBlock) {
if b.Func != thenb.fn || b.Func != elseb.fn { 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 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. // Phi represents a phi node.