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

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

View File

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

View File

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