Skip to content

Commit ad13f0e

Browse files
committed
runtime: tighten recover to direct deferred calls
1 parent ee864e0 commit ad13f0e

13 files changed

Lines changed: 438 additions & 178 deletions

File tree

cl/compile.go

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -152,23 +152,24 @@ type pkgInfo struct {
152152
type none = struct{}
153153

154154
type context struct {
155-
prog llssa.Program
156-
pkg llssa.Package
157-
fn llssa.Function
158-
goFn *ssa.Function
159-
fset *token.FileSet
160-
goProg *ssa.Program
161-
goTyps *types.Package
162-
goPkg *ssa.Package
163-
pyMod string
164-
skips map[string]none
165-
loaded map[*types.Package]*pkgInfo // loaded packages
166-
bvals map[ssa.Value]llssa.Expr // block values
167-
vargs map[*ssa.Alloc][]llssa.Expr // varargs
168-
funcs map[*ssa.Function]llssa.Function
169-
stackDefers map[*ssa.Function]bool
170-
anonDefers map[*ssa.Function]bool
171-
paramDIVars map[*types.Var]llssa.DIVar
155+
prog llssa.Program
156+
pkg llssa.Package
157+
fn llssa.Function
158+
goFn *ssa.Function
159+
fset *token.FileSet
160+
goProg *ssa.Program
161+
goTyps *types.Package
162+
goPkg *ssa.Package
163+
pyMod string
164+
skips map[string]none
165+
loaded map[*types.Package]*pkgInfo // loaded packages
166+
bvals map[ssa.Value]llssa.Expr // block values
167+
vargs map[*ssa.Alloc][]llssa.Expr // varargs
168+
funcs map[*ssa.Function]llssa.Function
169+
stackDefers map[*ssa.Function]bool
170+
anonDefers map[*ssa.Function]bool
171+
paramDIVars map[*types.Var]llssa.DIVar
172+
recoverSlots map[*ssa.Alloc]none
172173

173174
patches Patches
174175
blkInfos []blocks.Info
@@ -402,10 +403,13 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
402403
}
403404
if fn == nil {
404405
fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx, isInstance(f))
405-
if disableInline {
406+
if disableInline || functionUsesRecover(f) {
406407
fn.Inline(llssa.NoInline)
407408
}
408409
}
410+
if functionUsesRecover(f) {
411+
fn.Expr = fn.Expr.MarkMayRecover()
412+
}
409413
p.funcs[f] = fn
410414
isCgo := isCgoExternSymbol(f)
411415
if nblk := len(f.Blocks); nblk > 0 {
@@ -434,11 +438,18 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
434438
dbgSymsEnabled := enableDbgSyms && (f == nil || f.Origin() == nil)
435439
p.inits = append(p.inits, func() {
436440
oldFn, oldGoFn := p.fn, p.goFn
441+
oldRecoverSlots := p.recoverSlots
437442
p.fn = fn
438443
p.goFn = f
439444
p.state = state // restore pkgState when compiling funcBody
445+
if f.Recover != nil {
446+
p.recoverSlots = make(map[*ssa.Alloc]none)
447+
} else {
448+
p.recoverSlots = nil
449+
}
440450
defer func() {
441451
p.fn, p.goFn = oldFn, oldGoFn
452+
p.recoverSlots = oldRecoverSlots
442453
}()
443454
p.phis = nil
444455
if dbgSymsEnabled {
@@ -772,6 +783,29 @@ func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool {
772783
return false
773784
}
774785

786+
func (p *context) markRecoverSlot(v *ssa.Alloc) {
787+
if p.recoverSlots == nil || v.Heap {
788+
return
789+
}
790+
p.recoverSlots[v] = none{}
791+
}
792+
793+
func (p *context) isRecoverSlotAddr(v ssa.Value) bool {
794+
if p.recoverSlots == nil {
795+
return false
796+
}
797+
switch v := v.(type) {
798+
case *ssa.Alloc:
799+
_, ok := p.recoverSlots[v]
800+
return ok
801+
case *ssa.FieldAddr:
802+
return p.isRecoverSlotAddr(v.X)
803+
case *ssa.IndexAddr:
804+
return p.isRecoverSlotAddr(v.X)
805+
}
806+
return false
807+
}
808+
775809
func isAllocVargs(ctx *context, v *ssa.Alloc) bool {
776810
refs := *v.Referrers()
777811
n := len(refs)
@@ -905,6 +939,11 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
905939
x := p.compileValue(b, v.X)
906940
if v.Op == token.ARROW {
907941
ret = b.Recv(x, v.CommaOk)
942+
} else if v.Op == token.MUL {
943+
ret = b.Load(x)
944+
if p.isRecoverSlotAddr(v.X) {
945+
ret = ret.SetVolatile(true)
946+
}
908947
} else {
909948
ret = b.UnOp(v.Op, x)
910949
}
@@ -926,6 +965,10 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
926965
}
927966
elem := p.type_(t.Elem(), llssa.InGo)
928967
ret = b.Alloc(elem, v.Heap)
968+
p.markRecoverSlot(v)
969+
if p.isRecoverSlotAddr(v) {
970+
b.Store(ret, p.prog.Zero(elem)).SetVolatile(true)
971+
}
929972
case *ssa.IndexAddr:
930973
vx := v.X
931974
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index
@@ -1126,7 +1169,10 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
11261169
}
11271170
ptr := p.compileValue(b, va)
11281171
val := p.compileValue(b, v.Val)
1129-
b.Store(ptr, val)
1172+
store := b.Store(ptr, val)
1173+
if p.isRecoverSlotAddr(va) {
1174+
store.SetVolatile(true)
1175+
}
11301176
case *ssa.Jump:
11311177
jmpb := p.jumpTo(v)
11321178
b.Jump(jmpb)
@@ -1192,6 +1238,25 @@ func (p *context) getLocalVariable(b llssa.Builder, fn *ssa.Function, v *types.V
11921238
return b.DIVarAuto(scope, pos, v.Name(), t)
11931239
}
11941240

1241+
func functionUsesRecover(fn *ssa.Function) bool {
1242+
if fn == nil {
1243+
return false
1244+
}
1245+
for _, block := range fn.Blocks {
1246+
for _, instr := range block.Instrs {
1247+
call, ok := instr.(ssa.CallInstruction)
1248+
if !ok {
1249+
continue
1250+
}
1251+
builtin, ok := call.Common().Value.(*ssa.Builtin)
1252+
if ok && builtin.Name() == "recover" {
1253+
return true
1254+
}
1255+
}
1256+
}
1257+
return false
1258+
}
1259+
11951260
func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyObjRef, kind int) {
11961261
// TODO(xsw) v.Pkg == nil: means auto generated function?
11971262
if v.Pkg == p.goPkg || v.Pkg == nil {

cl/instr.go

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -838,16 +838,43 @@ func (p *context) deferStackOwner(fn *ssa.Function) llssa.Function {
838838
return owner
839839
}
840840

841-
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 {
841+
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 {
842842
if ds != nil {
843-
b.DeferTo(ds.owner, ds.stack, fn, buildCall, args...)
843+
b.DeferToRecover(ds.owner, ds.stack, mayRecover, fn, buildCall, args...)
844844
return llssa.Nil
845845
}
846-
return b.Do(act, fn, buildCall, args...)
846+
switch act {
847+
case llssa.Call, llssa.Go:
848+
return b.Do(act, fn, buildCall, args...)
849+
default:
850+
b.DeferRecover(act, mayRecover, fn, buildCall, args...)
851+
return llssa.Nil
852+
}
853+
}
854+
855+
func (p *context) callMayRecover(v ssa.Value) bool {
856+
switch v := v.(type) {
857+
case *ssa.Builtin:
858+
return false
859+
case *ssa.Function:
860+
return functionUsesRecover(v)
861+
case *ssa.MakeClosure:
862+
if fn, ok := v.Fn.(*ssa.Function); ok {
863+
return functionUsesRecover(fn)
864+
}
865+
return true
866+
case *ssa.Call:
867+
if fn := v.Call.StaticCallee(); fn != nil {
868+
return functionUsesRecover(fn)
869+
}
870+
return true
871+
}
872+
return true
847873
}
848874

849875
func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon, ds *explicitDeferStack) (ret llssa.Expr) {
850876
cv := call.Value
877+
mayRecover := p.callMayRecover(cv)
851878
if mthd := call.Method; mthd != nil {
852879
o := p.compileValue(b, cv)
853880
fn := b.Imethod(o, mthd)
@@ -856,7 +883,7 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
856883
hasVArg = fnHasVArg
857884
}
858885
args := p.compileValues(b, call.Args, hasVArg)
859-
ret = p.emitDo(b, act, ds, fn, llssa.Builder.Call, args...)
886+
ret = p.emitDo(b, act, ds, true, fn, llssa.Builder.Call, args...)
860887
return
861888
}
862889
kind := p.funcKind(cv)
@@ -879,7 +906,7 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
879906
}
880907
}
881908
args := p.compileValues(b, args, kind)
882-
ret = p.emitDo(b, act, ds, llssa.Builtin(fn), llssa.Builder.Call, args...)
909+
ret = p.emitDo(b, act, ds, false, llssa.Builtin(fn), llssa.Builder.Call, args...)
883910
case *ssa.Function:
884911
aFn, pyFn, ftype := p.compileFunction(cv)
885912
// TODO(xsw): check ca != llssa.Call
@@ -888,13 +915,13 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
888915
p.inCFunc = true
889916
args := p.compileValues(b, args, kind)
890917
p.inCFunc = false
891-
ret = p.emitDo(b, act, ds, aFn.Expr, llssa.Builder.Call, args...)
918+
ret = p.emitDo(b, act, ds, mayRecover, aFn.Expr, llssa.Builder.Call, args...)
892919
case goFunc:
893920
args := p.compileValues(b, args, kind)
894-
ret = p.emitDo(b, act, ds, aFn.Expr, llssa.Builder.Call, args...)
921+
ret = p.emitDo(b, act, ds, mayRecover, aFn.Expr, llssa.Builder.Call, args...)
895922
case pyFunc:
896923
args := p.compileValues(b, args, kind)
897-
ret = p.emitDo(b, act, ds, pyFn.Expr, llssa.Builder.Call, args...)
924+
ret = p.emitDo(b, act, ds, mayRecover, pyFn.Expr, llssa.Builder.Call, args...)
898925
case llgoPyList:
899926
args := p.compileValues(b, args, fnHasVArg)
900927
ret = b.PyList(args...)
@@ -968,33 +995,33 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
968995
b.Unreachable()
969996
case llgoAtomicLoad:
970997
args := p.compileValues(b, args, kind)
971-
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
998+
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
972999
return p.atomicLoad(b, args)
9731000
}, args...)
9741001
case llgoAtomicStore:
9751002
args := p.compileValues(b, args, kind)
976-
p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
1003+
p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
9771004
return p.atomicStore(b, args)
9781005
}, args...)
9791006
case llgoAtomicCmpXchg:
9801007
args := p.compileValues(b, args, kind)
981-
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
1008+
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
9821009
return p.atomicCmpXchg(b, args)
9831010
}, args...)
9841011
case llgoAtomicCmpXchgOK:
9851012
args := p.compileValues(b, args, kind)
986-
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
1013+
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
9871014
return p.atomicCmpXchgOK(b, args)
9881015
}, args...)
9891016
case llgoAtomicAddReturnNew:
9901017
args := p.compileValues(b, args, kind)
991-
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
1018+
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
9921019
return b.BinOp(token.ADD, p.atomic(b, llssa.OpAdd, args), args[1])
9931020
}, args...)
9941021
default:
9951022
if ftype >= llgoAtomicOpBase && ftype <= llgoAtomicOpLast {
9961023
args := p.compileValues(b, args, kind)
997-
ret = p.emitDo(b, act, ds, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
1024+
ret = p.emitDo(b, act, ds, false, llssa.Nil, func(b llssa.Builder, _ llssa.Expr, args ...llssa.Expr) llssa.Expr {
9981025
return p.atomic(b, llssa.AtomicOp(ftype-llgoAtomicOpBase), args)
9991026
}, args...)
10001027
} else {
@@ -1004,7 +1031,7 @@ func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallComm
10041031
default:
10051032
fn := p.compileValue(b, cv)
10061033
args := p.compileValues(b, args, kind)
1007-
ret = p.emitDo(b, act, ds, fn, llssa.Builder.Call, args...)
1034+
ret = p.emitDo(b, act, ds, mayRecover, fn, llssa.Builder.Call, args...)
10081035
}
10091036
return
10101037
}

cl/rewrite_internal_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,8 @@ func TestEmitDoWithExplicitDeferStack(t *testing.T) {
328328
b.SetBlockEx(owner.Block(0), llssa.BeforeLast, true)
329329

330330
ctx := &context{}
331-
ctx.emitDo(b, llssa.DeferInLoop, &explicitDeferStack{stack: stack, owner: owner}, callee.Expr, llssa.Builder.Call)
332-
ctx.emitDo(b, llssa.DeferAlways, nil, callee.Expr, llssa.Builder.Call)
331+
ctx.emitDo(b, llssa.DeferInLoop, &explicitDeferStack{stack: stack, owner: owner}, false, callee.Expr, llssa.Builder.Call)
332+
ctx.emitDo(b, llssa.DeferAlways, nil, false, callee.Expr, llssa.Builder.Call)
333333
b.DeferStackDrain()
334334
b.RunDefers()
335335
b.Return()
@@ -465,7 +465,7 @@ func TestEmitDoWithoutExplicitDeferStack(t *testing.T) {
465465
b := fn.MakeBody(1)
466466

467467
ctx := &context{}
468-
got := ctx.emitDo(b, llssa.Call, nil, callee.Expr, llssa.Builder.Call)
468+
got := ctx.emitDo(b, llssa.Call, nil, false, callee.Expr, llssa.Builder.Call)
469469
if got.IsNil() {
470470
t.Fatal("emitDo without explicit defer stack should return direct call result")
471471
}

runtime/internal/runtime/z_default.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
package runtime
44

55
import (
6+
"unsafe"
7+
68
c "github.com/goplus/llgo/runtime/internal/clite"
79
"github.com/goplus/llgo/runtime/internal/clite/debug"
810
"github.com/goplus/llgo/runtime/internal/clite/pthread"
@@ -16,11 +18,12 @@ var (
1618

1719
// Rethrow rethrows a panic.
1820
func Rethrow(link *Defer) {
19-
if ptr := excepKey.Get(); ptr != nil {
21+
if ptr := panicKey.Get(); ptr != nil {
2022
if link == nil {
21-
TracePanic(*(*any)(ptr))
23+
node := (*panicNode)(ptr)
24+
TracePanic(node.arg)
2225
debug.PrintStack(2)
23-
c.Free(ptr)
26+
c.Free(unsafe.Pointer(node))
2427
c.Exit(2)
2528
} else {
2629
c.Siglongjmp(link.Addr, 1)

0 commit comments

Comments
 (0)