diff --git a/c/c.go b/c/c.go index cfa9a77c..9726879f 100644 --- a/c/c.go +++ b/c/c.go @@ -57,9 +57,6 @@ func Alloca(size uintptr) Pointer //go:linkname AllocaCStr llgo.allocaCStr func AllocaCStr(s string) *Char -//go:linkname Unreachable llgo.unreachable -func Unreachable() - //go:linkname Malloc C.malloc func Malloc(size uintptr) Pointer @@ -84,6 +81,20 @@ func Remove(path *Char) Int // ----------------------------------------------------------------------------- +//go:linkname AllocaSigjmpBuf llgo.sigjmpbuf +func AllocaSigjmpBuf() Pointer + +//go:linkname Sigsetjmp llgo.sigsetjmp +func Sigsetjmp(jb Pointer, savemask Int) Int + +//go:linkname Siglongjmp llgo.siglongjmp +func Siglongjmp(jb Pointer, retval Int) + +//go:linkname Unreachable llgo.unreachable +func Unreachable() + +// ----------------------------------------------------------------------------- + //go:linkname Exit C.exit func Exit(Int) diff --git a/cl/_testlibc/setjmp/in.go b/cl/_testlibc/setjmp/in.go new file mode 100644 index 00000000..90367ce8 --- /dev/null +++ b/cl/_testlibc/setjmp/in.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/goplus/llgo/c" +) + +func main() { + jb := c.AllocaSigjmpBuf() + switch ret := c.Sigsetjmp(jb, 0); ret { + case 0: + cstr := c.Str("?Hello, setjmp!\n") + c.Fprintf(c.Stderr, c.Advance(cstr, 1)) + c.Siglongjmp(jb, 1) + default: + println("exception:", ret) + } +} diff --git a/cl/_testlibc/setjmp/out.ll b/cl/_testlibc/setjmp/out.ll new file mode 100644 index 00000000..1c8a0e79 --- /dev/null +++ b/cl/_testlibc/setjmp/out.ll @@ -0,0 +1 @@ +; \ No newline at end of file diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 160aa1b3..a25d6548 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -80,52 +80,24 @@ func TestErrCompileInstrOrValue(t *testing.T) { ctx.compileInstrOrValue(nil, &ssa.Call{}, true) } -func TestErrAdvance(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fatal("advance: no error?") - } - }() - var ctx context - ctx.advance(nil, nil) -} - -func TestErrAlloca(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fatal("alloca: no error?") - } - }() - var ctx context - ctx.alloca(nil, nil) -} - -func TestErrAllocaCStr(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fatal("allocaCStr: no error?") - } - }() - var ctx context - ctx.allocaCStr(nil, nil) -} - -func TestCStrNoArgs(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fatal("cstr: no error?") - } - }() - cstr(nil, nil) -} - -func TestCStrNonconst(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fatal("cstr: no error?") - } - }() - cstr(nil, []ssa.Value{&ssa.Parameter{}}) +func TestErrBuiltin(t *testing.T) { + test := func(builtin string, fn func(ctx *context)) { + defer func() { + if r := recover(); r == nil { + t.Fatal(builtin, ": no error?") + } + }() + var ctx context + fn(&ctx) + } + test("advance", func(ctx *context) { ctx.advance(nil, nil) }) + test("alloca", func(ctx *context) { ctx.alloca(nil, nil) }) + test("allocaCStr", func(ctx *context) { ctx.allocaCStr(nil, nil) }) + test("stringData", func(ctx *context) { ctx.stringData(nil, nil) }) + test("sigsetjmp", func(ctx *context) { ctx.sigsetjmp(nil, nil) }) + test("siglongjmp", func(ctx *context) { ctx.siglongjmp(nil, nil) }) + test("cstr(NoArgs)", func(ctx *context) { cstr(nil, nil) }) + test("cstr(Nonconst)", func(ctx *context) { cstr(nil, []ssa.Value{&ssa.Parameter{}}) }) } func TestPkgNoInit(t *testing.T) { diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 04d1036b..6b1208c1 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -116,7 +116,7 @@ func testFrom(t *testing.T, pkgDir, sel string, byLLGen bool) { } expected := string(b) if byLLGen { - if v := llgen.GenFrom(in); v != expected { + if v := llgen.GenFrom(in); v != expected && expected != ";" { // expected == ";" means skipping out.ll t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected) } } else { @@ -152,7 +152,7 @@ func TestCompileEx(t *testing.T, src any, fname, expected string) { ret.PyInit() } - if v := ret.String(); v != expected { + if v := ret.String(); v != expected && expected != ";" { // expected == ";" means skipping out.ll t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected) } } diff --git a/cl/compile.go b/cl/compile.go index 469cd296..4239cd76 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -332,6 +332,12 @@ func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObj ftype = llgoStringData case "pyList": ftype = llgoPyList + case "sigjmpbuf": + ftype = llgoSigjmpbuf + case "sigsetjmp": + ftype = llgoSigsetjmp + case "siglongjmp": + ftype = llgoSiglongjmp case "unreachable": ftype = llgoUnreachable default: @@ -512,6 +518,25 @@ func (p *context) stringData(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) panic("stringData(s string): invalid arguments") } +func (p *context) sigsetjmp(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { + if len(args) == 2 { + jb := p.compileValue(b, args[0]) + savemask := p.compileValue(b, args[1]) + return b.Sigsetjmp(jb, savemask) + } + panic("sigsetjmp(jb c.SigjmpBuf, savemask c.Int): invalid arguments") +} + +func (p *context) siglongjmp(b llssa.Builder, args []ssa.Value) { + if len(args) == 2 { + jb := p.compileValue(b, args[0]) + retval := p.compileValue(b, args[1]) + b.Siglongjmp(jb, retval) + return + } + panic("siglongjmp(jb c.SigjmpBuf, retval c.Int): invalid arguments") +} + func isPhi(i ssa.Instruction) bool { _, ok := i.(*ssa.Phi) return ok @@ -611,6 +636,12 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon ret = p.allocaCStr(b, args) case llgoStringData: ret = p.stringData(b, args) + case llgoSigsetjmp: + ret = p.sigsetjmp(b, args) + case llgoSiglongjmp: + p.siglongjmp(b, args) + case llgoSigjmpbuf: // func sigjmpbuf() + ret = b.AllocaSigjmpBuf() case llgoUnreachable: // func unreachable() b.Unreachable() default: diff --git a/cl/import.go b/cl/import.go index 35ef5317..7cd23813 100644 --- a/cl/import.go +++ b/cl/import.go @@ -309,6 +309,9 @@ const ( llgoIndex = llgoInstrBase + 5 llgoStringData = llgoInstrBase + 6 llgoPyList = llgoInstrBase + 7 + llgoSigjmpbuf = llgoInstrBase + 10 + llgoSigsetjmp = llgoInstrBase + 11 + llgoSiglongjmp = llgoInstrBase + 12 ) func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, string, int) { diff --git a/ssa/cl_test.go b/ssa/cl_test.go index 947d2eab..70f7f455 100644 --- a/ssa/cl_test.go +++ b/ssa/cl_test.go @@ -33,6 +33,10 @@ func TestFromTestpy(t *testing.T) { cltest.FromDir(t, "", "../cl/_testpy", false) } +func TestFromTestlibc(t *testing.T) { + cltest.FromDir(t, "", "../cl/_testlibc", true) +} + func TestFromTestrt(t *testing.T) { cltest.FromDir(t, "", "../cl/_testrt", true) } diff --git a/ssa/eh.go b/ssa/eh.go index 47b69e90..f6beca0c 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -16,15 +16,64 @@ package ssa +// #include +import "C" + import ( "go/token" + "go/types" "log" + "unsafe" "github.com/goplus/llvm" ) // ----------------------------------------------------------------------------- +type sigjmpbuf = C.sigjmp_buf + +// func(env unsafe.Pointer, savemask c.Int) c.Int +func (p Program) tySigsetjmp() *types.Signature { + if p.sigsetjmpTy == nil { + paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type) + paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type) + params := types.NewTuple(paramPtr, paramCInt) + results := types.NewTuple(paramCInt) + p.sigsetjmpTy = types.NewSignatureType(nil, nil, nil, params, results, false) + } + return p.sigsetjmpTy +} + +// func(env unsafe.Pointer, retval c.Int) +func (p Program) tySiglongjmp() *types.Signature { + if p.sigljmpTy == nil { + paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type) + paramCInt := types.NewParam(token.NoPos, nil, "", p.CInt().raw.Type) + params := types.NewTuple(paramPtr, paramCInt) + p.sigljmpTy = types.NewSignatureType(nil, nil, nil, params, nil, false) + } + return p.sigljmpTy +} + +func (b Builder) AllocaSigjmpBuf() Expr { + prog := b.Prog + n := unsafe.Sizeof(sigjmpbuf{}) + size := prog.IntVal(uint64(n), prog.Uintptr()) + return b.Alloca(size) +} + +func (b Builder) Sigsetjmp(jb, savemask Expr) Expr { + fn := b.Pkg.cFunc("sigsetjmp", b.Prog.tySigsetjmp()) + return b.Call(fn, jb, savemask) +} + +func (b Builder) Siglongjmp(jb, retval Expr) { + fn := b.Pkg.cFunc("siglongjmp", b.Prog.tySiglongjmp()) + b.Call(fn, jb, retval) +} + +// ----------------------------------------------------------------------------- + const ( deferKey = "__llgo_defer" ) diff --git a/ssa/package.go b/ssa/package.go index 762c3843..24171164 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -179,7 +179,8 @@ type aProgram struct { setSpecTy *types.Signature routineTy *types.Signature destructTy *types.Signature - //deferFnTy *types.Signature + sigsetjmpTy *types.Signature + sigljmpTy *types.Signature paramObjPtr_ *types.Var