From bfa4e08a4e5f01ac5d67eb6aeb37ba75bc932674 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 21 Jun 2024 23:44:56 +0800 Subject: [PATCH] cl: c.Func (llgo.funcAddr); demo: cppintf (how to use c++ interface) --- _demo/cppintf/cppintf.go | 37 ++++++++++++++ _demo/cppintf/foo/bar/bar.cpp | 11 +++++ _demo/cppintf/foo/foo.go | 22 +++++++++ _demo/cppintf/foo/llgo_autogen.lla | Bin 0 -> 410 bytes c/c.go | 3 ++ cl/_testlibc/complex/in.go | 8 +-- cl/_testlibc/complex/out.ll | 75 +++++++++++++++++------------ cl/builtin_test.go | 1 + cl/compile.go | 9 +++- cl/import.go | 1 + cl/instr.go | 17 +++++++ 11 files changed, 149 insertions(+), 35 deletions(-) create mode 100644 _demo/cppintf/cppintf.go create mode 100644 _demo/cppintf/foo/bar/bar.cpp create mode 100644 _demo/cppintf/foo/foo.go create mode 100644 _demo/cppintf/foo/llgo_autogen.lla diff --git a/_demo/cppintf/cppintf.go b/_demo/cppintf/cppintf.go new file mode 100644 index 000000000..91ab6f7cc --- /dev/null +++ b/_demo/cppintf/cppintf.go @@ -0,0 +1,37 @@ +package main + +import ( + "github.com/goplus/llgo/_demo/cppintf/foo" + "github.com/goplus/llgo/c" +) + +type Bar struct { + foo.Callback + a int + b float64 +} + +func NewBar(a int, b float64) *Bar { + return &Bar{ + Callback: foo.Callback{ + Vptr: &foo.CallbackVtbl{ + ValA: c.Func((*Bar).getA), + ValB: c.Func((*Bar).getB), + }, + }, + a: a, b: b, + } +} + +func (p *Bar) getA() int { + return p.a +} + +func (p *Bar) getB() float64 { + return p.b +} + +func main() { + bar := NewBar(1, 2.0) + foo.F(&bar.Callback) +} diff --git a/_demo/cppintf/foo/bar/bar.cpp b/_demo/cppintf/foo/bar/bar.cpp new file mode 100644 index 000000000..d78c0b843 --- /dev/null +++ b/_demo/cppintf/foo/bar/bar.cpp @@ -0,0 +1,11 @@ +#include +#define interface struct + +interface ICallback { + virtual int valA() = 0; + virtual double valB() = 0; +}; + +extern "C" void f(ICallback* cb) { + printf("Hello %d, %lf!\n", cb->valA(), cb->valB()); +} diff --git a/_demo/cppintf/foo/foo.go b/_demo/cppintf/foo/foo.go new file mode 100644 index 000000000..8393f78ea --- /dev/null +++ b/_demo/cppintf/foo/foo.go @@ -0,0 +1,22 @@ +package foo + +import ( + "unsafe" +) + +const ( + LLGoFiles = "bar/bar.cpp" + LLGoPackage = "link" +) + +type Callback struct { + Vptr *CallbackVtbl +} + +type CallbackVtbl struct { + ValA unsafe.Pointer + ValB unsafe.Pointer +} + +//go:linkname F C.f +func F(cb *Callback) diff --git a/_demo/cppintf/foo/llgo_autogen.lla b/_demo/cppintf/foo/llgo_autogen.lla new file mode 100644 index 0000000000000000000000000000000000000000..ecc48cee64c666c309f997b68d4f8798af0f6cf9 GIT binary patch literal 410 zcmWIWW@Zs#U|`^2NZoriqV9Iy@n=BZJSGMPeg+waoSgLh_{7qZ{Pfg3y_}rT5Kac> zzWJqT{Xkq=!Og(P@|BT+0c^n9iN4-P3JZfn!Y>7J?F?;sfv%Lc4v%9~a+g+2|-L-3_fVYhnQ#r$8jYRgYIbRC| zoE8_#J)AkWjr;NJ*U}Y&hl32is54&pChEGTgtb(lXpc#u=)X(M5|5Maev_RbHF3e; z2eS2hU#q-+$};=Bq?60sRbOUZ3QssCQ<(TH;)DG1A1nW_tp3xT7yHTlW5>_G(nnWU yFb8-uGRZOH3NQ&^&@nIoLxf>TBZ!3&e5{b*Lkqe9Z&o&taz-Ew1=9K;6Bq!Hr<+ay literal 0 HcmV?d00001 diff --git a/c/c.go b/c/c.go index 5e66959f8..e66406f77 100644 --- a/c/c.go +++ b/c/c.go @@ -48,6 +48,9 @@ type integer interface { //go:linkname Str llgo.cstr func Str(string) *Char +//go:linkname Func llgo.funcAddr +func Func(any) Pointer + // llgo:link Advance llgo.advance func Advance[PtrT any, I integer](ptr PtrT, offset I) PtrT { return ptr } diff --git a/cl/_testlibc/complex/in.go b/cl/_testlibc/complex/in.go index 3bb8a0fdc..73f6bf970 100644 --- a/cl/_testlibc/complex/in.go +++ b/cl/_testlibc/complex/in.go @@ -1,10 +1,12 @@ package main import ( + "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/math/cmplx" ) -func f(c, z complex64) { +func f(c, z complex64, addr c.Pointer) { + println("addr:", addr) println("abs(3+4i):", cmplx.Absf(c)) println("real(3+4i):", real(z)) println("imag(3+4i):", imag(z)) @@ -14,6 +16,6 @@ func main() { re := float32(3.0) im := float32(4.0) z := complex64(3 + 4i) - c := complex(re, im) - f(c, z) + x := complex(re, im) + f(x, z, c.Func(f)) } diff --git a/cl/_testlibc/complex/out.ll b/cl/_testlibc/complex/out.ll index ae814dbed..f89cb11cd 100644 --- a/cl/_testlibc/complex/out.ll +++ b/cl/_testlibc/complex/out.ll @@ -4,49 +4,60 @@ source_filename = "main" %"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } @"main.init$guard" = global i1 false, align 1 -@0 = private unnamed_addr constant [10 x i8] c"abs(3+4i):", align 1 -@1 = private unnamed_addr constant [11 x i8] c"real(3+4i):", align 1 -@2 = private unnamed_addr constant [11 x i8] c"imag(3+4i):", align 1 +@0 = private unnamed_addr constant [5 x i8] c"addr:", align 1 +@1 = private unnamed_addr constant [10 x i8] c"abs(3+4i):", align 1 +@2 = private unnamed_addr constant [11 x i8] c"real(3+4i):", align 1 +@3 = private unnamed_addr constant [11 x i8] c"imag(3+4i):", align 1 @__llgo_argc = global i32 0, align 4 @__llgo_argv = global ptr null, align 8 -define void @main.f({ float, float } %0, { float, float } %1) { +define void @main.f({ float, float } %0, { float, float } %1, ptr %2) { _llgo_0: - %2 = call float @cabsf({ float, float } %0) %3 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 %4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 0 store ptr @0, ptr %4, align 8 %5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1 - store i64 10, ptr %5, align 4 + store i64 5, ptr %5, align 4 %6 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %3, align 8 call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %6) call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) - %7 = fpext float %2 to double - call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %7) + call void @"github.com/goplus/llgo/internal/runtime.PrintPointer"(ptr %2) call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) - %8 = extractvalue { float, float } %1, 0 - %9 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 - %10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %9, i32 0, i32 0 - store ptr @1, ptr %10, align 8 - %11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %9, i32 0, i32 1 - store i64 11, ptr %11, align 4 - %12 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %9, align 8 - call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %12) + %7 = call float @cabsf({ float, float } %0) + %8 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %8, i32 0, i32 0 + store ptr @1, ptr %9, align 8 + %10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %8, i32 0, i32 1 + store i64 10, ptr %10, align 4 + %11 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %8, align 8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %11) call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) - %13 = fpext float %8 to double - call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %13) + %12 = fpext float %7 to double + call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %12) call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) - %14 = extractvalue { float, float } %1, 1 - %15 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 - %16 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %15, i32 0, i32 0 - store ptr @2, ptr %16, align 8 - %17 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %15, i32 0, i32 1 - store i64 11, ptr %17, align 4 - %18 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %15, align 8 - call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %18) + %13 = extractvalue { float, float } %1, 0 + %14 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %15 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %14, i32 0, i32 0 + store ptr @2, ptr %15, align 8 + %16 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %14, i32 0, i32 1 + store i64 11, ptr %16, align 4 + %17 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %14, align 8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %17) call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) - %19 = fpext float %14 to double - call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %19) + %18 = fpext float %13 to double + call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %18) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %19 = extractvalue { float, float } %1, 1 + %20 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %21 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %20, i32 0, i32 0 + store ptr @3, ptr %21, align 8 + %22 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %20, i32 0, i32 1 + store i64 11, ptr %22, align 4 + %23 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %20, align 8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %23) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + %24 = fpext float %19 to double + call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %24) call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) ret void } @@ -76,16 +87,18 @@ _llgo_0: %4 = getelementptr inbounds { float, float }, ptr %2, i32 0, i32 1 store float 4.000000e+00, ptr %4, align 4 %5 = load { float, float }, ptr %2, align 4 - call void @main.f({ float, float } %5, { float, float } { float 3.000000e+00, float 4.000000e+00 }) + call void @main.f({ float, float } %5, { float, float } { float 3.000000e+00, float 4.000000e+00 }, ptr @main.f) ret i32 0 } -declare float @cabsf({ float, float }) - 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.PrintPointer"(ptr) + +declare float @cabsf({ float, float }) + declare void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double) declare void @"github.com/goplus/llgo/internal/runtime.init"() diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 8030d37e4..75604b3a5 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -139,6 +139,7 @@ func TestErrBuiltin(t *testing.T) { test("allocaCStr", func(ctx *context) { ctx.allocaCStr(nil, nil) }) test("string", func(ctx *context) { ctx.string(nil, nil) }) test("stringData", func(ctx *context) { ctx.stringData(nil, nil) }) + test("funcAddr", func(ctx *context) { ctx.funcAddr(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) }) diff --git a/cl/compile.go b/cl/compile.go index 8ebb96a7e..4517303e9 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -524,12 +524,19 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue ret = b.Slice(x, low, high, max) case *ssa.MakeInterface: if refs := *v.Referrers(); len(refs) == 1 { - if ref, ok := refs[0].(*ssa.Store); ok { + switch ref := refs[0].(type) { + case *ssa.Store: if va, ok := ref.Addr.(*ssa.IndexAddr); ok { if _, ok = p.isVArgs(va.X); ok { // varargs: this is a varargs store return } } + case *ssa.Call: + if fn, ok := ref.Call.Value.(*ssa.Function); ok { + if _, _, ftype := p.funcOf(fn); ftype == llgoFuncAddr { // llgo.funcAddr + return + } + } } } t := p.prog.Type(v.Type(), llssa.InGo) diff --git a/cl/import.go b/cl/import.go index f5f6f5c76..b9baec866 100644 --- a/cl/import.go +++ b/cl/import.go @@ -384,6 +384,7 @@ const ( llgoDeferData = llgoInstrBase + 6 llgoStringData = llgoInstrBase + 7 llgoString = llgoInstrBase + 8 + llgoFuncAddr = llgoInstrBase + 9 llgoSigjmpbuf = llgoInstrBase + 0xa llgoSigsetjmp = llgoInstrBase + 0xb diff --git a/cl/instr.go b/cl/instr.go index 7c19a9e09..73bdd6624 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -94,6 +94,20 @@ func (p *context) stringData(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) panic("stringData(s string): invalid arguments") } +// func funcAddr(fn any) unsafe.Pointer +func (p *context) funcAddr(b llssa.Builder, args []ssa.Value) llssa.Expr { + if len(args) == 1 { + if fn, ok := args[0].(*ssa.MakeInterface); ok { + if fnDecl, ok := fn.X.(*ssa.Function); ok { + if aFn, _, _ := p.compileFunction(fnDecl); aFn != nil { + return aFn.Expr + } + } + } + } + panic("funcAddr(): 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]) @@ -160,6 +174,7 @@ var llgoInstrs = map[string]int{ "allocaCStr": llgoAllocaCStr, "string": llgoString, "stringData": llgoStringData, + "funcAddr": llgoFuncAddr, "pyList": llgoPyList, "sigjmpbuf": llgoSigjmpbuf, "sigsetjmp": llgoSigsetjmp, @@ -327,6 +342,8 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon ret = b.AllocaSigjmpBuf() case llgoDeferData: // func deferData() *Defer ret = b.DeferData() + case llgoFuncAddr: + ret = p.funcAddr(b, args) case llgoUnreachable: // func unreachable() b.Unreachable() default: