Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 84 additions & 19 deletions cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,23 +152,24 @@ type pkgInfo struct {
type none = struct{}

type context struct {
prog llssa.Program
pkg llssa.Package
fn llssa.Function
goFn *ssa.Function
fset *token.FileSet
goProg *ssa.Program
goTyps *types.Package
goPkg *ssa.Package
pyMod string
skips map[string]none
loaded map[*types.Package]*pkgInfo // loaded packages
bvals map[ssa.Value]llssa.Expr // block values
vargs map[*ssa.Alloc][]llssa.Expr // varargs
funcs map[*ssa.Function]llssa.Function
stackDefers map[*ssa.Function]bool
anonDefers map[*ssa.Function]bool
paramDIVars map[*types.Var]llssa.DIVar
prog llssa.Program
pkg llssa.Package
fn llssa.Function
goFn *ssa.Function
fset *token.FileSet
goProg *ssa.Program
goTyps *types.Package
goPkg *ssa.Package
pyMod string
skips map[string]none
loaded map[*types.Package]*pkgInfo // loaded packages
bvals map[ssa.Value]llssa.Expr // block values
vargs map[*ssa.Alloc][]llssa.Expr // varargs
funcs map[*ssa.Function]llssa.Function
stackDefers map[*ssa.Function]bool
anonDefers map[*ssa.Function]bool
paramDIVars map[*types.Var]llssa.DIVar
recoverSlots map[*ssa.Alloc]none

patches Patches
blkInfos []blocks.Info
Expand Down Expand Up @@ -402,10 +403,13 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
}
if fn == nil {
fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx, isInstance(f))
if disableInline {
if disableInline || functionUsesRecover(f) {
fn.Inline(llssa.NoInline)
}
}
if functionUsesRecover(f) {
fn.Expr = fn.Expr.MarkMayRecover()
}
p.funcs[f] = fn
isCgo := isCgoExternSymbol(f)
if nblk := len(f.Blocks); nblk > 0 {
Expand Down Expand Up @@ -434,11 +438,18 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
dbgSymsEnabled := enableDbgSyms && (f == nil || f.Origin() == nil)
p.inits = append(p.inits, func() {
oldFn, oldGoFn := p.fn, p.goFn
oldRecoverSlots := p.recoverSlots
p.fn = fn
p.goFn = f
p.state = state // restore pkgState when compiling funcBody
if f.Recover != nil {
p.recoverSlots = make(map[*ssa.Alloc]none)
} else {
p.recoverSlots = nil
}
defer func() {
p.fn, p.goFn = oldFn, oldGoFn
p.recoverSlots = oldRecoverSlots
}()
p.phis = nil
if dbgSymsEnabled {
Expand Down Expand Up @@ -772,6 +783,29 @@ func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool {
return false
}

func (p *context) markRecoverSlot(v *ssa.Alloc) {
if p.recoverSlots == nil || v.Heap {
return
}
p.recoverSlots[v] = none{}
}

func (p *context) isRecoverSlotAddr(v ssa.Value) bool {
if p.recoverSlots == nil {
return false
}
switch v := v.(type) {
case *ssa.Alloc:
_, ok := p.recoverSlots[v]
return ok
case *ssa.FieldAddr:
return p.isRecoverSlotAddr(v.X)
case *ssa.IndexAddr:
return p.isRecoverSlotAddr(v.X)
}
return false
}

func isAllocVargs(ctx *context, v *ssa.Alloc) bool {
refs := *v.Referrers()
n := len(refs)
Expand Down Expand Up @@ -905,6 +939,11 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
x := p.compileValue(b, v.X)
if v.Op == token.ARROW {
ret = b.Recv(x, v.CommaOk)
} else if v.Op == token.MUL {
ret = b.Load(x)
if p.isRecoverSlotAddr(v.X) {
ret = ret.SetVolatile(true)
}
} else {
ret = b.UnOp(v.Op, x)
}
Expand All @@ -926,6 +965,10 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
}
elem := p.type_(t.Elem(), llssa.InGo)
ret = b.Alloc(elem, v.Heap)
p.markRecoverSlot(v)
if p.isRecoverSlotAddr(v) {
b.Store(ret, p.prog.Zero(elem)).SetVolatile(true)
}
case *ssa.IndexAddr:
vx := v.X
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index
Expand Down Expand Up @@ -1126,7 +1169,10 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
}
ptr := p.compileValue(b, va)
val := p.compileValue(b, v.Val)
b.Store(ptr, val)
store := b.Store(ptr, val)
if p.isRecoverSlotAddr(va) {
store.SetVolatile(true)
}
case *ssa.Jump:
jmpb := p.jumpTo(v)
b.Jump(jmpb)
Expand Down Expand Up @@ -1192,6 +1238,25 @@ func (p *context) getLocalVariable(b llssa.Builder, fn *ssa.Function, v *types.V
return b.DIVarAuto(scope, pos, v.Name(), t)
}

func functionUsesRecover(fn *ssa.Function) bool {
if fn == nil {
return false
}
for _, block := range fn.Blocks {
for _, instr := range block.Instrs {
call, ok := instr.(ssa.CallInstruction)
if !ok {
continue
}
builtin, ok := call.Common().Value.(*ssa.Builtin)
if ok && builtin.Name() == "recover" {
return true
}
}
}
return false
}

func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyObjRef, kind int) {
// TODO(xsw) v.Pkg == nil: means auto generated function?
if v.Pkg == p.goPkg || v.Pkg == nil {
Expand Down
57 changes: 42 additions & 15 deletions cl/instr.go
Original file line number Diff line number Diff line change
Expand Up @@ -838,16 +838,43 @@ func (p *context) deferStackOwner(fn *ssa.Function) llssa.Function {
return owner
}

func (p *context) emitDo(b llssa.Builder, act llssa.DoAction, ds *explicitDeferStack, fn llssa.Expr, buildCall func(llssa.Builder, llssa.Expr, ...llssa.Expr) llssa.Expr, args ...llssa.Expr) llssa.Expr {
func (p *context) emitDo(b llssa.Builder, act llssa.DoAction, ds *explicitDeferStack, mayRecover bool, fn llssa.Expr, buildCall func(llssa.Builder, llssa.Expr, ...llssa.Expr) llssa.Expr, args ...llssa.Expr) llssa.Expr {
if ds != nil {
b.DeferTo(ds.owner, ds.stack, fn, buildCall, args...)
b.DeferToRecover(ds.owner, ds.stack, mayRecover, fn, buildCall, args...)
return llssa.Nil
}
return b.Do(act, fn, buildCall, args...)
switch act {
case llssa.Call, llssa.Go:
return b.Do(act, fn, buildCall, args...)
default:
b.DeferRecover(act, mayRecover, fn, buildCall, args...)
return llssa.Nil
}
}

func (p *context) callMayRecover(v ssa.Value) bool {
switch v := v.(type) {
case *ssa.Builtin:
return false
case *ssa.Function:
return functionUsesRecover(v)
case *ssa.MakeClosure:
if fn, ok := v.Fn.(*ssa.Function); ok {
return functionUsesRecover(fn)
}
return true
case *ssa.Call:
if fn := v.Call.StaticCallee(); fn != nil {
return functionUsesRecover(fn)
}
return true
}
return true
}

func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon, ds *explicitDeferStack) (ret llssa.Expr) {
cv := call.Value
mayRecover := p.callMayRecover(cv)
if mthd := call.Method; mthd != nil {
o := p.compileValue(b, cv)
fn := b.Imethod(o, mthd)
Expand All @@ -856,7 +883,7 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
hasVArg = fnHasVArg
}
args := p.compileValues(b, call.Args, hasVArg)
ret = p.emitDo(b, act, ds, fn, llssa.Builder.Call, args...)
ret = p.emitDo(b, act, ds, true, fn, llssa.Builder.Call, args...)
return
}
kind := p.funcKind(cv)
Expand All @@ -879,7 +906,7 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
}
}
args := p.compileValues(b, args, kind)
ret = p.emitDo(b, act, ds, llssa.Builtin(fn), llssa.Builder.Call, args...)
ret = p.emitDo(b, act, ds, false, llssa.Builtin(fn), llssa.Builder.Call, args...)
case *ssa.Function:
aFn, pyFn, ftype := p.compileFunction(cv)
// TODO(xsw): check ca != llssa.Call
Expand All @@ -888,13 +915,13 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
p.inCFunc = true
args := p.compileValues(b, args, kind)
p.inCFunc = false
ret = p.emitDo(b, act, ds, aFn.Expr, llssa.Builder.Call, args...)
ret = p.emitDo(b, act, ds, mayRecover, aFn.Expr, llssa.Builder.Call, args...)
case goFunc:
args := p.compileValues(b, args, kind)
ret = p.emitDo(b, act, ds, aFn.Expr, llssa.Builder.Call, args...)
ret = p.emitDo(b, act, ds, mayRecover, aFn.Expr, llssa.Builder.Call, args...)
case pyFunc:
args := p.compileValues(b, args, kind)
ret = p.emitDo(b, act, ds, pyFn.Expr, llssa.Builder.Call, args...)
ret = p.emitDo(b, act, ds, mayRecover, pyFn.Expr, llssa.Builder.Call, args...)
case llgoPyList:
args := p.compileValues(b, args, fnHasVArg)
ret = b.PyList(args...)
Expand Down Expand Up @@ -968,33 +995,33 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
b.Unreachable()
case llgoAtomicLoad:
args := p.compileValues(b, args, kind)
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
return p.atomicLoad(b, args)
}, args...)
case llgoAtomicStore:
args := p.compileValues(b, args, kind)
p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
return p.atomicStore(b, args)
}, args...)
case llgoAtomicCmpXchg:
args := p.compileValues(b, args, kind)
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
return p.atomicCmpXchg(b, args)
}, args...)
case llgoAtomicCmpXchgOK:
args := p.compileValues(b, args, kind)
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
return p.atomicCmpXchgOK(b, args)
}, args...)
case llgoAtomicAddReturnNew:
args := p.compileValues(b, args, kind)
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
return b.BinOp(token.ADD, p.atomic(b, llssa.OpAdd, args), args[1])
}, args...)
default:
if ftype >= llgoAtomicOpBase && ftype <= llgoAtomicOpLast {
args := p.compileValues(b, args, kind)
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
return p.atomic(b, llssa.AtomicOp(ftype-llgoAtomicOpBase), args)
}, args...)
} else {
Expand All @@ -1004,7 +1031,7 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
default:
fn := p.compileValue(b, cv)
args := p.compileValues(b, args, kind)
ret = p.emitDo(b, act, ds, fn, llssa.Builder.Call, args...)
ret = p.emitDo(b, act, ds, mayRecover, fn, llssa.Builder.Call, args...)
}
return
}
Expand Down
6 changes: 3 additions & 3 deletions cl/rewrite_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,8 @@ func TestEmitDoWithExplicitDeferStack(t *testing.T) {
b.SetBlockEx(owner.Block(0), llssa.BeforeLast, true)

ctx := &context{}
ctx.emitDo(b, llssa.DeferInLoop, &explicitDeferStack{stack: stack, owner: owner}, callee.Expr, llssa.Builder.Call)
ctx.emitDo(b, llssa.DeferAlways, nil, callee.Expr, llssa.Builder.Call)
ctx.emitDo(b, llssa.DeferInLoop, &explicitDeferStack{stack: stack, owner: owner}, false, callee.Expr, llssa.Builder.Call)
ctx.emitDo(b, llssa.DeferAlways, nil, false, callee.Expr, llssa.Builder.Call)
b.DeferStackDrain()
b.RunDefers()
b.Return()
Expand Down Expand Up @@ -465,7 +465,7 @@ func TestEmitDoWithoutExplicitDeferStack(t *testing.T) {
b := fn.MakeBody(1)

ctx := &context{}
got := ctx.emitDo(b, llssa.Call, nil, callee.Expr, llssa.Builder.Call)
got := ctx.emitDo(b, llssa.Call, nil, false, callee.Expr, llssa.Builder.Call)
if got.IsNil() {
t.Fatal("emitDo without explicit defer stack should return direct call result")
}
Expand Down
9 changes: 6 additions & 3 deletions runtime/internal/runtime/z_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
package runtime

import (
"unsafe"

c "github.com/goplus/llgo/runtime/internal/clite"
"github.com/goplus/llgo/runtime/internal/clite/debug"
"github.com/goplus/llgo/runtime/internal/clite/pthread"
Expand All @@ -16,11 +18,12 @@ var (

// Rethrow rethrows a panic.
func Rethrow(link *Defer) {
if ptr := excepKey.Get(); ptr != nil {
if ptr := panicKey.Get(); ptr != nil {
if link == nil {
TracePanic(*(*any)(ptr))
node := (*panicNode)(ptr)
TracePanic(node.arg)
debug.PrintStack(2)
c.Free(ptr)
c.Free(unsafe.Pointer(node))
c.Exit(2)
} else {
c.Siglongjmp(link.Addr, 1)
Expand Down
Loading
Loading