diff --git a/cl/_testgo/cgofull/cgofull.go b/cl/_testgo/cgofull/cgofull.go index 476b0167..0380d127 100644 --- a/cl/_testgo/cgofull/cgofull.go +++ b/cl/_testgo/cgofull/cgofull.go @@ -77,9 +77,26 @@ static void test_macros() { #define MY_VERSION "1.0.0" #define MY_CODE 0x12345678 -void test_void() { +static void test_void() { printf("test_void\n"); } + +typedef int (*Cb)(int); + +extern int go_callback(int); + +extern int c_callback(int i); + +static void test_callback(Cb cb) { + printf("test_callback, cb: %p, go_callback: %p, c_callback: %p\n", cb, go_callback, c_callback); + printf("test_callback, *cb: %p, *go_callback: %p, *c_callback: %p\n", *(void**)cb, *(void**)(go_callback), *(void**)(c_callback)); + printf("cb result: %d\n", cb(123)); + printf("done\n"); +} + +static void run_callback() { + test_callback(c_callback); +} */ import "C" import ( @@ -90,6 +107,11 @@ import ( "github.com/goplus/llgo/_demo/cgofull/pymod2" ) +//export go_callback +func go_callback(i C.int) C.int { + return i + 1 +} + func main() { runPy() f := &C.Foo{a: 1} @@ -104,6 +126,16 @@ func main() { fmt.Println(C.MY_VERSION) fmt.Println(int(C.MY_CODE)) C.test_void() + + println("call run_callback") + C.run_callback() + + // test _Cgo_ptr and _cgoCheckResult + println("call with go_callback") + C.test_callback((C.Cb)(C.go_callback)) + + println("call with c_callback") + C.test_callback((C.Cb)(C.c_callback)) } func runPy() { @@ -112,4 +144,6 @@ func runPy() { Run("print('Hello, Python!')") C.PyObject_Print((*C.PyObject)(unsafe.Pointer(pymod1.Float(1.23))), C.stderr, 0) C.PyObject_Print((*C.PyObject)(unsafe.Pointer(pymod2.Long(123))), C.stdout, 0) + // test _Cgo_use + C.PyObject_Print((*C.PyObject)(unsafe.Pointer(C.PyComplex_FromDoubles(C.double(1.23), C.double(4.56)))), C.stdout, 0) } diff --git a/cl/_testgo/cgofull/foo.c b/cl/_testgo/cgofull/foo.c index b3f07e04..aaa25c6a 100644 --- a/cl/_testgo/cgofull/foo.c +++ b/cl/_testgo/cgofull/foo.c @@ -1,6 +1,12 @@ #include #include "foo.h" -void print_foo(Foo* f) { +void print_foo(Foo *f) +{ printf("print_foo: %d\n", f->a); } + +int c_callback(int i) +{ + return i + 1; +} \ No newline at end of file diff --git a/cl/_testgo/out.ll b/cl/_testgo/out.ll deleted file mode 100644 index 36088807..00000000 --- a/cl/_testgo/out.ll +++ /dev/null @@ -1,64 +0,0 @@ -; ModuleID = 'main' -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 [5 x i8] c"hello", align 1 -@__llgo_argc = global i32 0, align 4 -@__llgo_argv = global ptr null, align 8 - -define i64 @main.Foo(%"github.com/goplus/llgo/internal/runtime.String" %0) { -_llgo_0: - %1 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %0, 1 - ret i64 %1 -} - -define void @main.Test() { -_llgo_0: - br label %_llgo_3 - -_llgo_1: ; preds = %_llgo_3 - %0 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 - %1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 0 - store ptr @0, ptr %1, align 8 - %2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 1 - store i64 5, ptr %2, align 4 - %3 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %0, align 8 - %4 = call i64 @main.Foo(%"github.com/goplus/llgo/internal/runtime.String" %3) - %5 = add i64 %6, 1 - br label %_llgo_3 - -_llgo_2: ; preds = %_llgo_3 - ret void - -_llgo_3: ; preds = %_llgo_1, %_llgo_0 - %6 = phi i64 [ 0, %_llgo_0 ], [ %5, %_llgo_1 ] - %7 = icmp slt i64 %6, 1000000 - br i1 %7, label %_llgo_1, label %_llgo_2 -} - -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() - call void @main.Test() - ret i32 0 -} - -declare void @"github.com/goplus/llgo/internal/runtime.init"() diff --git a/cl/_testrt/strlen/out.ll b/cl/_testrt/strlen/out.ll index bd201f26..b6339870 100644 --- a/cl/_testrt/strlen/out.ll +++ b/cl/_testrt/strlen/out.ll @@ -1,12 +1,23 @@ ; ModuleID = 'main' source_filename = "main" +%"github.com/goplus/llgo/internal/runtime.eface" = type { ptr, ptr } + @"github.com/goplus/llgo/internal/runtime.cgoAlwaysFalse" = external global i1, align 1 @main.format = global [10 x i8] zeroinitializer, align 1 @"main.init$guard" = global i1 false, align 1 @__llgo_argc = global i32 0, align 4 @__llgo_argv = global ptr null, align 8 +define ptr @main._Cgo_ptr(ptr %0) { +_llgo_0: + ret ptr %0 +} + +declare void @runtime.cgoUse(%"github.com/goplus/llgo/internal/runtime.eface") + +declare void @runtime.cgoCheckResult(%"github.com/goplus/llgo/internal/runtime.eface") + define void @main.init() { _llgo_0: %0 = load i1, ptr @"main.init$guard", align 1 diff --git a/cl/_testrt/struct/out.ll b/cl/_testrt/struct/out.ll index bdf1e248..ec060663 100644 --- a/cl/_testrt/struct/out.ll +++ b/cl/_testrt/struct/out.ll @@ -2,6 +2,7 @@ source_filename = "main" %main.Foo = type { i32, i1 } +%"github.com/goplus/llgo/internal/runtime.eface" = type { ptr, ptr } @"github.com/goplus/llgo/internal/runtime.cgoAlwaysFalse" = external global i1, align 1 @main.format = global [10 x i8] zeroinitializer, align 1 @@ -35,6 +36,15 @@ _llgo_0: ret void } +define ptr @main._Cgo_ptr(ptr %0) { +_llgo_0: + ret ptr %0 +} + +declare void @runtime.cgoUse(%"github.com/goplus/llgo/internal/runtime.eface") + +declare void @runtime.cgoCheckResult(%"github.com/goplus/llgo/internal/runtime.eface") + define void @main.init() { _llgo_0: %0 = load i1, ptr @"main.init$guard", align 1 diff --git a/cl/_testrt/typalias/out.ll b/cl/_testrt/typalias/out.ll index beb826c5..54c4b87e 100644 --- a/cl/_testrt/typalias/out.ll +++ b/cl/_testrt/typalias/out.ll @@ -1,6 +1,8 @@ ; ModuleID = 'main' source_filename = "main" +%"github.com/goplus/llgo/internal/runtime.eface" = type { ptr, ptr } + @"github.com/goplus/llgo/internal/runtime.cgoAlwaysFalse" = external global i1, align 1 @main.format = global [10 x i8] zeroinitializer, align 1 @"main.init$guard" = global i1 false, align 1 @@ -23,6 +25,15 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } +define ptr @main._Cgo_ptr(ptr %0) { +_llgo_0: + ret ptr %0 +} + +declare void @runtime.cgoUse(%"github.com/goplus/llgo/internal/runtime.eface") + +declare void @runtime.cgoCheckResult(%"github.com/goplus/llgo/internal/runtime.eface") + define void @main.init() { _llgo_0: %0 = load i1, ptr @"main.init$guard", align 1 diff --git a/cl/compile.go b/cl/compile.go index 8c9eca46..eb4f1fcb 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -189,7 +189,7 @@ var ( argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8])) ) -func isCgoCfuncOrCmacro(f *ssa.Function) bool { +func isCgoExternSymbol(f *ssa.Function) bool { name := f.Name() return isCgoCfunc(name) || isCgoCmacro(name) } @@ -202,6 +202,10 @@ func isCgoCmacro(name string) bool { return strings.HasPrefix(name, "_Cmacro_") } +func isCgoVar(name string) bool { + return strings.HasPrefix(name, "__cgo_") +} + func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Function, llssa.PyObjRef, int) { pkgTypes, name, ftype := p.funcName(f, true) if ftype != goFunc { @@ -255,7 +259,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun fn.Inline(llssa.NoInline) } } - isCgo := isCgoCfuncOrCmacro(f) + isCgo := isCgoExternSymbol(f) if nblk := len(f.Blocks); nblk > 0 { p.cgoCalled = false p.cgoArgs = nil @@ -918,7 +922,17 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { } return pyFn.Expr case *ssa.Global: + varName := v.Name() val := p.varOf(b, v) + if isCgoVar(varName) { + fname := p.fset.Position(v.Pos()).Filename + funcs, ok := p.cgoFuncs[fname] + if !ok { + funcs = make([]string, 0, 1) + } + funcs = append(funcs, val.Name()) + p.cgoFuncs[fname] = funcs + } if debugSymbols { pos := p.fset.Position(v.Pos()) b.DIGlobal(val, v.Name(), pos) @@ -1053,6 +1067,26 @@ func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files [ fn() } externs = ctx.cgoFuncs + // TODO(lijie): read export name + for _, funcs := range externs { + for _, funcName := range funcs { + if strings.Contains(funcName, ".__cgo_") { + goFnName := strings.Replace(funcName, ".__cgo_", ".", 1) + idx := strings.LastIndex(funcName, ".__cgo_") + cfuncName := funcName[idx+len(".__cgo_"):] + v := ret.VarOf(funcName) + if fn := ret.FuncOf(goFnName); fn != nil { + // TODO(lijie): naive go:export, need better way from comment + fn.SetName(cfuncName) + // Replace symbol instead of static linking + v.ReplaceAllUsesWith(fn.Expr) + } else if fn := ret.FuncOf(cfuncName); fn != nil { + // Replace symbol instead of static linking + v.ReplaceAllUsesWith(fn.Expr) + } + } + } + } return } diff --git a/cl/import.go b/cl/import.go index b6877fa5..643950b9 100644 --- a/cl/import.go +++ b/cl/import.go @@ -448,7 +448,7 @@ func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, strin if checkCgo(fname) && !cgoIgnored(fname) { return nil, fname, llgoInstr } - if isCgoCfuncOrCmacro(fn) { + if isCgoExternSymbol(fn) { if _, ok := llgoInstrs[fname]; ok { return nil, fname, llgoInstr } diff --git a/internal/build/build.go b/internal/build/build.go index e12e57eb..25a0ff75 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -525,6 +525,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoLdflags []string, cl.SetDebug(0) } check(err) + aPkg.LPkg = ret cgoLdflags, err = buildCgo(ctx, aPkg, aPkg.Package.Syntax, externs, verbose) if needLLFile(ctx.mode) { pkg.ExportFile += ".ll" @@ -533,7 +534,6 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) (cgoLdflags []string, fmt.Fprintf(os.Stderr, "==> Export %s: %s\n", aPkg.PkgPath, pkg.ExportFile) } } - aPkg.LPkg = ret return } diff --git a/internal/build/cgo.go b/internal/build/cgo.go index ddbeb376..ac2b31e8 100644 --- a/internal/build/cgo.go +++ b/internal/build/cgo.go @@ -21,6 +21,7 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "os" "os/exec" "path/filepath" @@ -29,6 +30,7 @@ import ( "github.com/goplus/llgo/internal/buildtags" "github.com/goplus/llgo/internal/safesplit" + llssa "github.com/goplus/llgo/ssa" ) type cgoDecl struct { @@ -93,12 +95,21 @@ func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string mallocFix := false for _, symbols := range externs { for _, symbolName := range symbols { - if m := re.FindStringSubmatch(symbolName); len(m) > 0 { - cgoSymbols[symbolName] = m[3] - pkgPrefix := m[1] + lastPart := symbolName + lastDot := strings.LastIndex(symbolName, ".") + if lastDot != -1 { + lastPart = symbolName[lastDot+1:] + } + if strings.HasPrefix(lastPart, "__cgo_") { + // func ptr var: main.__cgo_func_name + cgoSymbols[symbolName] = lastPart + } else if m := re.FindStringSubmatch(symbolName); len(m) > 0 { + prefix := m[1] // _cgo_hash_(Cfunc|Cmacro)_ + name := m[3] // remaining part + cgoSymbols[symbolName] = name // fix missing _cgo_9113e32b6599_Cfunc__Cmalloc if !mallocFix && m[2] == "Cfunc" { - mallocName := pkgPrefix + "_Cmalloc" + mallocName := prefix + "_Cmalloc" cgoSymbols[mallocName] = "_Cmalloc" mallocFix = true } @@ -113,7 +124,7 @@ func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string tmpName := tmpFile.Name() defer os.Remove(tmpName) code := cgoHeader + "\n\n" + preamble.src - externDecls, err := genExternDeclsByClang(code, cflags, cgoSymbols) + externDecls, err := genExternDeclsByClang(pkg, code, cflags, cgoSymbols) if err != nil { return nil, err } @@ -137,7 +148,7 @@ type clangASTNode struct { Inner []clangASTNode `json:"inner,omitempty"` } -func genExternDeclsByClang(src string, cflags []string, cgoSymbols map[string]string) (string, error) { +func genExternDeclsByClang(pkg *aPackage, src string, cflags []string, cgoSymbols map[string]string) (string, error) { tmpSrc, err := os.CreateTemp("", "cgo-src-*.c") if err != nil { return "", err @@ -158,25 +169,37 @@ func genExternDeclsByClang(src string, cflags []string, cgoSymbols map[string]st b := strings.Builder{} var toRemove []string for cgoName, symbolName := range cgoSymbols { - if symbolNames[symbolName] { - b.WriteString(fmt.Sprintf("void* %s = (void*)%s;\n", cgoName, symbolName)) + if strings.HasPrefix(symbolName, "__cgo_") { + cfuncName := symbolName[len("__cgo_"):] + cfn := pkg.LPkg.NewFunc(cfuncName, types.NewSignature(nil, nil, nil, false), llssa.InC) + cgoVar := pkg.LPkg.VarOf(cgoName) + cgoVar.ReplaceAllUsesWith(cfn.Expr) toRemove = append(toRemove, cgoName) - } else if macroNames[symbolName] { + } else { + usePtr := "" + if symbolNames[symbolName] { + usePtr = "*" + } else if !macroNames[symbolName] { + continue + } /* template: - typeof(stdout) _cgo_1574167f3838_Cmacro_stdout; + typeof(fputs)* _cgo_1574167f3838_Cfunc_fputs; __attribute__((constructor)) - static void _init__cgo_1574167f3838_Cmacro_stdout() { - _cgo_1574167f3838_Cmacro_stdout = stdout; + static void _init__cgo_1574167f3838_Cfunc_fputs() { + _cgo_1574167f3838_Cfunc_fputs = fputs; }*/ b.WriteString(fmt.Sprintf(` -typeof(%s) %s; +typeof(%s)%s %s; __attribute__((constructor)) static void _init_%s() { %s = %s; } -`, symbolName, cgoName, cgoName, cgoName, symbolName)) +`, + symbolName, usePtr, cgoName, + cgoName, + cgoName, symbolName)) toRemove = append(toRemove, cgoName) } } diff --git a/ssa/decl.go b/ssa/decl.go index 165b5094..d139c7cb 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -117,6 +117,10 @@ func (g Global) InitNil() { g.impl.SetInitializer(llvm.ConstNull(g.impl.GlobalValueType())) } +func (g Global) ReplaceAllUsesWith(v Expr) { + g.impl.ReplaceAllUsesWith(v.impl) +} + // ----------------------------------------------------------------------------- // Function represents the parameters, results, and code of a function diff --git a/ssa/expr.go b/ssa/expr.go index f8fa7365..7b4b0b01 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -59,6 +59,15 @@ func (v Expr) SetOrdering(ordering AtomicOrdering) Expr { return v } +func (v Expr) SetName(alias string) Expr { + v.impl.SetName(alias) + return v +} + +func (v Expr) Name() string { + return v.impl.Name() +} + // ----------------------------------------------------------------------------- type builtinTy struct {