cgo: supports c/go callback funcs
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
@@ -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"()
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user