From 4abcbb9b5116152936d3ec794eb5190751c2d534 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 13 Jun 2024 13:51:36 +0800 Subject: [PATCH] recover --- c/c.go | 8 +++++--- cl/_testgo/defer3/in.go | 1 + cl/_testgo/defer4/in.go | 29 +++++++++++++++++++++++++++++ cl/_testgo/defer4/out.ll | 1 + cl/compile.go | 6 +++++- internal/runtime/z_rt.go | 30 +++++++++++++++++++++--------- ssa/decl.go | 6 ++++++ ssa/eh.go | 11 +++++------ ssa/expr.go | 4 ++-- 9 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 cl/_testgo/defer4/in.go create mode 100644 cl/_testgo/defer4/out.ll diff --git a/c/c.go b/c/c.go index 55addd60..4c5f4e13 100644 --- a/c/c.go +++ b/c/c.go @@ -103,6 +103,11 @@ func Qsort(base Pointer, count, elem uintptr, compar func(a, b Pointer) Int) // ----------------------------------------------------------------------------- +//go:linkname Atoi C.atoi +func Atoi(s *Char) Int + +// ----------------------------------------------------------------------------- + //go:linkname Printf C.printf func Printf(format *Char, __llgo_va_list ...any) Int @@ -171,6 +176,3 @@ func GetoptLong(argc Int, argv **Char, optstring *Char, longopts *Option, longin func GetoptLongOnly(argc Int, argv **Char, optstring *Char, longopts *Option, longindex *Int) Int // ----------------------------------------------------------------------------- - -//go:linkname Atoi C.atoi -func Atoi(s *Char) Int diff --git a/cl/_testgo/defer3/in.go b/cl/_testgo/defer3/in.go index 7398dedd..3679f48f 100644 --- a/cl/_testgo/defer3/in.go +++ b/cl/_testgo/defer3/in.go @@ -20,4 +20,5 @@ func main() { return } fail() + println("unreachable") } diff --git a/cl/_testgo/defer4/in.go b/cl/_testgo/defer4/in.go new file mode 100644 index 00000000..fee7dd63 --- /dev/null +++ b/cl/_testgo/defer4/in.go @@ -0,0 +1,29 @@ +package main + +func f(s string) bool { + return len(s) > 2 +} + +func fail() { + defer println("bye") + defer func() { + if e := recover(); e != nil { + println("recover:", e.(string)) + } + }() + panic("panic message") +} + +func main() { + defer func() { + println("hi") + }() + if s := "hello"; f(s) { + defer println(s) + } else { + defer println("world") + return + } + fail() + println("reachable") +} diff --git a/cl/_testgo/defer4/out.ll b/cl/_testgo/defer4/out.ll new file mode 100644 index 00000000..1c8a0e79 --- /dev/null +++ b/cl/_testgo/defer4/out.ll @@ -0,0 +1 @@ +; \ No newline at end of file diff --git a/cl/compile.go b/cl/compile.go index e71609dc..568a236f 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -555,7 +555,11 @@ func isPhi(i ssa.Instruction) bool { } func (p *context) compilePhis(b llssa.Builder, block *ssa.BasicBlock) int { - ret := p.fn.Block(block.Index) + fn := p.fn + ret := fn.Block(block.Index) + if block.Comment == "recover" { // set recover block + fn.SetRecover(ret) + } b.SetBlockEx(ret, llssa.AtEnd, false) if ninstr := len(block.Instrs); ninstr > 0 { if isPhi(block.Instrs[0]) { diff --git a/internal/runtime/z_rt.go b/internal/runtime/z_rt.go index 063bb25a..222c6e5c 100644 --- a/internal/runtime/z_rt.go +++ b/internal/runtime/z_rt.go @@ -34,10 +34,21 @@ type Defer struct { Rund unsafe.Pointer // block address after RunDefers } +// Recover recovers a panic. +func Recover() (ret any) { + ptr := excepKey.Get() + if ptr != nil { + excepKey.Set(nil) + ret = *(*any)(ptr) + c.Free(ptr) + } + return +} + // Panic panics with a value. -func Panic(v Eface) { +func Panic(v any) { ptr := c.Malloc(unsafe.Sizeof(v)) - *(*Eface)(ptr) = v + *(*any)(ptr) = v excepKey.Set(ptr) Rethrow((*Defer)(c.GoDeferData())) @@ -45,13 +56,14 @@ func Panic(v Eface) { // Rethrow rethrows a panic. func Rethrow(link *Defer) { - if link == nil { - ptr := excepKey.Get() - TracePanic(*(*Eface)(ptr)) - c.Free(ptr) - c.Exit(2) - } else { - c.Siglongjmp(link.Addr, 1) + if ptr := excepKey.Get(); ptr != nil { + if link == nil { + TracePanic(*(*Eface)(ptr)) + c.Free(ptr) + c.Exit(2) + } else { + c.Siglongjmp(link.Addr, 1) + } } } diff --git a/ssa/decl.go b/ssa/decl.go index 83a3f57c..637d6aa4 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -179,6 +179,7 @@ type aFunction struct { blks []BasicBlock defer_ *aDefer + recov BasicBlock params []Type freeVars Expr @@ -329,4 +330,9 @@ func (p Function) Block(idx int) BasicBlock { return p.blks[idx] } +// SetRecover sets the recover block for the function. +func (p Function) SetRecover(blk BasicBlock) { + p.recov = blk +} + // ----------------------------------------------------------------------------- diff --git a/ssa/eh.go b/ssa/eh.go index e4ade559..3daa4c72 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -22,6 +22,7 @@ import "C" import ( "go/token" "go/types" + "log" "unsafe" "github.com/goplus/llvm" @@ -174,7 +175,7 @@ func (b Builder) getDefer(kind DoAction) *aDefer { b.SetBlockEx(rethrowBlk, AtEnd, false) // rethrow b.Call(b.Pkg.rtFunc("Rethrow"), link) - b.Unreachable() // TODO: func supports noreturn attribute + b.Jump(self.recov) if kind == DeferAlways { b.SetBlockEx(next, AtEnd, false) @@ -266,16 +267,14 @@ func (b Builder) Unreachable() { b.impl.CreateUnreachable() } -/* // Recover emits a recover instruction. -func (b Builder) Recover() (v Expr) { +func (b Builder) Recover() Expr { if debugInstr { log.Println("Recover") } - prog := b.Prog - return prog.Zero(prog.Any()) + // TODO(xsw): recover can't be a function call in Go + return b.Call(b.Pkg.rtFunc("Recover")) } -*/ // Panic emits a panic instruction. func (b Builder) Panic(v Expr) { diff --git a/ssa/expr.go b/ssa/expr.go index 08c4d5c2..f41ff644 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -898,8 +898,8 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { } } } - //case "recover": - // return b.Recover() + case "recover": + return b.Recover() case "print", "println": return b.PrintEx(fn == "println", args...) }