From 93be634673098f0f50343ebbbf5db2856d938542 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 8 Jun 2024 14:49:48 +0800 Subject: [PATCH 1/5] builtin: sigjmpbuf/sigsetjmp/siglongjmp --- c/c.go | 17 +++++++-- cl/_testlibc/setjmp/in.go | 17 +++++++++ cl/_testlibc/setjmp/out.ll | 73 ++++++++++++++++++++++++++++++++++++++ cl/compile.go | 31 ++++++++++++++++ cl/import.go | 3 ++ ssa/eh.go | 49 +++++++++++++++++++++++++ ssa/package.go | 3 +- 7 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 cl/_testlibc/setjmp/in.go create mode 100644 cl/_testlibc/setjmp/out.ll 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..937a1b67 --- /dev/null +++ b/cl/_testlibc/setjmp/out.ll @@ -0,0 +1,73 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } + +@"main.init$guard" = global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = global ptr null +@0 = private unnamed_addr constant [17 x i8] c"?Hello, setjmp!\0A\00", align 1 +@__stderrp = external global ptr +@1 = private unnamed_addr constant [11 x i8] c"exception:\00", align 1 + +define void @main.init() { +_llgo_0: + %0 = load i1, ptr @"main.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"main.init$guard", align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define i32 @main(i32 %0, ptr %1) { +_llgo_0: + store i32 %0, ptr @__llgo_argc, align 4 + store ptr %1, ptr @__llgo_argv, align 8 + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %2 = alloca i8, i64 196, align 1 + %3 = call i32 @sigsetjmp(ptr %2, i32 0) + %4 = icmp eq i32 %3, 0 + br i1 %4, label %_llgo_2, label %_llgo_3 + +_llgo_1: ; preds = %_llgo_3, %_llgo_2 + ret i32 0 + +_llgo_2: ; preds = %_llgo_0 + %5 = load ptr, ptr @__stderrp, align 8 + %6 = call i32 (ptr, ptr, ...) @fprintf(ptr %5, ptr getelementptr (i8, ptr @0, i64 1)) + call void @siglongjmp(ptr %2, i32 1) + br label %_llgo_1 + +_llgo_3: ; preds = %_llgo_0 + %7 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %7, i32 0, i32 0 + store ptr @1, ptr %8, align 8 + %9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %7, i32 0, i32 1 + store i64 10, ptr %9, align 4 + %10 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %7, align 8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %10) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + %11 = sext i32 %3 to i64 + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %11) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_1 +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare i32 @sigsetjmp(ptr, i32) + +declare i32 @fprintf(ptr, ptr, ...) + +declare void @siglongjmp(ptr, i32) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String") + +declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64) 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/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 From 02e0651eabd06c22f7b64d953d17ea201cf5d114 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 8 Jun 2024 14:59:26 +0800 Subject: [PATCH 2/5] ignore setjmp/out.ll (os deps) --- cl/_testlibc/setjmp/out.ll | 74 +------------------------------------- cl/cltest/cltest.go | 2 +- 2 files changed, 2 insertions(+), 74 deletions(-) diff --git a/cl/_testlibc/setjmp/out.ll b/cl/_testlibc/setjmp/out.ll index 937a1b67..1c8a0e79 100644 --- a/cl/_testlibc/setjmp/out.ll +++ b/cl/_testlibc/setjmp/out.ll @@ -1,73 +1 @@ -; ModuleID = 'main' -source_filename = "main" - -%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } - -@"main.init$guard" = global ptr null -@__llgo_argc = global ptr null -@__llgo_argv = global ptr null -@0 = private unnamed_addr constant [17 x i8] c"?Hello, setjmp!\0A\00", align 1 -@__stderrp = external global ptr -@1 = private unnamed_addr constant [11 x i8] c"exception:\00", align 1 - -define void @main.init() { -_llgo_0: - %0 = load i1, ptr @"main.init$guard", align 1 - br i1 %0, label %_llgo_2, label %_llgo_1 - -_llgo_1: ; preds = %_llgo_0 - store i1 true, ptr @"main.init$guard", align 1 - br label %_llgo_2 - -_llgo_2: ; preds = %_llgo_1, %_llgo_0 - ret void -} - -define i32 @main(i32 %0, ptr %1) { -_llgo_0: - store i32 %0, ptr @__llgo_argc, align 4 - store ptr %1, ptr @__llgo_argv, align 8 - call void @"github.com/goplus/llgo/internal/runtime.init"() - call void @main.init() - %2 = alloca i8, i64 196, align 1 - %3 = call i32 @sigsetjmp(ptr %2, i32 0) - %4 = icmp eq i32 %3, 0 - br i1 %4, label %_llgo_2, label %_llgo_3 - -_llgo_1: ; preds = %_llgo_3, %_llgo_2 - ret i32 0 - -_llgo_2: ; preds = %_llgo_0 - %5 = load ptr, ptr @__stderrp, align 8 - %6 = call i32 (ptr, ptr, ...) @fprintf(ptr %5, ptr getelementptr (i8, ptr @0, i64 1)) - call void @siglongjmp(ptr %2, i32 1) - br label %_llgo_1 - -_llgo_3: ; preds = %_llgo_0 - %7 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 - %8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %7, i32 0, i32 0 - store ptr @1, ptr %8, align 8 - %9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %7, i32 0, i32 1 - store i64 10, ptr %9, align 4 - %10 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %7, align 8 - call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %10) - call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) - %11 = sext i32 %3 to i64 - call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %11) - call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) - br label %_llgo_1 -} - -declare void @"github.com/goplus/llgo/internal/runtime.init"() - -declare i32 @sigsetjmp(ptr, i32) - -declare i32 @fprintf(ptr, ptr, ...) - -declare void @siglongjmp(ptr, i32) - -declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String") - -declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) - -declare void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64) +; \ No newline at end of file diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 04d1036b..3b7fe1b8 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -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) } } From e6ab5bd86d05755972c83015229e36404b4d0f8c Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 8 Jun 2024 15:04:35 +0800 Subject: [PATCH 3/5] testFrom: expected == ';' means skipping out.ll --- cl/cltest/cltest.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 3b7fe1b8..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 { From 4e98055b9c6933c740b994608d66e809607eb788 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 8 Jun 2024 15:09:24 +0800 Subject: [PATCH 4/5] TestFromTestlibc --- ssa/cl_test.go | 4 ++++ 1 file changed, 4 insertions(+) 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) } From abf461a0493582a5fd355652eb3119f53c59886d Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 8 Jun 2024 15:17:49 +0800 Subject: [PATCH 5/5] TestErrBuiltin --- cl/builtin_test.go | 64 +++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 46 deletions(-) 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) {