cgo: supports //export functions only calls in C

This commit is contained in:
Li Jie
2024-11-27 18:22:23 +08:00
parent e46b3e24d6
commit c3407eac5e
4 changed files with 64 additions and 68 deletions

View File

@@ -94,8 +94,11 @@ static void test_callback(Cb cb) {
printf("done\n"); printf("done\n");
} }
extern int go_callback_not_use_in_go(int);
static void run_callback() { static void run_callback() {
test_callback(c_callback); test_callback(c_callback);
test_callback(go_callback_not_use_in_go);
} }
*/ */
import "C" import "C"
@@ -107,6 +110,11 @@ import (
"github.com/goplus/llgo/cl/_testgo/cgofull/pymod2" "github.com/goplus/llgo/cl/_testgo/cgofull/pymod2"
) )
//export go_callback_not_use_in_go
func go_callback_not_use_in_go(i C.int) C.int {
return i + 1
}
//export go_callback //export go_callback
func go_callback(i C.int) C.int { func go_callback(i C.int) C.int {
return i + 1 return i + 1

View File

@@ -113,7 +113,8 @@ type context struct {
cgoCalled bool cgoCalled bool
cgoArgs []llssa.Expr cgoArgs []llssa.Expr
cgoRet llssa.Expr cgoRet llssa.Expr
cgoFuncs map[string][]string cgoSymbols []string
cgoExports map[string]string
} }
type pkgState byte type pkgState byte
@@ -415,14 +416,6 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
callRuntimeInit(b, pkg) callRuntimeInit(b, pkg)
b.Call(pkg.FuncOf("main.init").Expr) b.Call(pkg.FuncOf("main.init").Expr)
} }
fname := p.goProg.Fset.Position(block.Parent().Pos()).Filename
if p.cgoFuncs == nil {
p.cgoFuncs = make(map[string][]string)
}
var cgoFuncs []string
if funcs, ok := p.cgoFuncs[fname]; ok {
cgoFuncs = funcs
}
fnName := block.Parent().Name() fnName := block.Parent().Name()
cgoReturned := false cgoReturned := false
isCgoCfunc := isCgoCfunc(fnName) isCgoCfunc := isCgoCfunc(fnName)
@@ -442,8 +435,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
// load cgo function pointer // load cgo function pointer
varName := instr.X.Name() varName := instr.X.Name()
if instr.Op == token.MUL && strings.HasPrefix(varName, "_cgo_") { if instr.Op == token.MUL && strings.HasPrefix(varName, "_cgo_") {
cgoFuncs = append(cgoFuncs, varName) p.cgoSymbols = append(p.cgoSymbols, varName)
p.cgoFuncs[fname] = cgoFuncs
p.compileInstr(b, instr) p.compileInstr(b, instr)
} }
case *ssa.Call: case *ssa.Call:
@@ -925,13 +917,7 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
varName := v.Name() varName := v.Name()
val := p.varOf(b, v) val := p.varOf(b, v)
if isCgoVar(varName) { if isCgoVar(varName) {
fname := p.fset.Position(v.Pos()).Filename p.cgoSymbols = append(p.cgoSymbols, val.Name())
funcs, ok := p.cgoFuncs[fname]
if !ok {
funcs = make([]string, 0, 1)
}
funcs = append(funcs, val.Name())
p.cgoFuncs[fname] = funcs
} }
if debugSymbols { if debugSymbols {
pos := p.fset.Position(v.Pos()) pos := p.fset.Position(v.Pos())
@@ -1001,7 +987,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
} }
// NewPackageEx compiles a Go package to LLVM IR package. // NewPackageEx compiles a Go package to LLVM IR package.
func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs map[string][]string, err error) { func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs []string, err error) {
pkgProg := pkg.Prog pkgProg := pkg.Prog
pkgTypes := pkg.Pkg pkgTypes := pkg.Pkg
oldTypes := pkgTypes oldTypes := pkgTypes
@@ -1033,6 +1019,8 @@ func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files [
loaded: map[*types.Package]*pkgInfo{ loaded: map[*types.Package]*pkgInfo{
types.Unsafe: {kind: PkgDeclOnly}, // TODO(xsw): PkgNoInit or PkgDeclOnly? types.Unsafe: {kind: PkgDeclOnly}, // TODO(xsw): PkgNoInit or PkgDeclOnly?
}, },
cgoExports: make(map[string]string),
cgoSymbols: make([]string, 0, 128),
} }
ctx.initPyModule() ctx.initPyModule()
ctx.initFiles(pkgPath, files) ctx.initFiles(pkgPath, files)
@@ -1066,25 +1054,11 @@ func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files [
ctx.initAfter = nil ctx.initAfter = nil
fn() fn()
} }
externs = ctx.cgoFuncs externs = ctx.cgoSymbols
// TODO(lijie): read export name for fnName, exportName := range ctx.cgoExports {
for _, funcs := range externs { fn := ret.FuncOf(fnName)
for _, funcName := range funcs { if fn != nil {
if strings.Contains(funcName, ".__cgo_") { fn.SetName(exportName)
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 return

View File

@@ -249,6 +249,7 @@ func (p *context) initLinkname(line string, f func(inPkgName string) (fullName s
linkname = "//go:linkname " linkname = "//go:linkname "
llgolink = "//llgo:link " llgolink = "//llgo:link "
llgolink2 = "// llgo:link " llgolink2 = "// llgo:link "
exportName = "//export "
) )
if strings.HasPrefix(line, linkname) { if strings.HasPrefix(line, linkname) {
p.initLink(line, len(linkname), f) p.initLink(line, len(linkname), f)
@@ -256,6 +257,15 @@ func (p *context) initLinkname(line string, f func(inPkgName string) (fullName s
p.initLink(line, len(llgolink2), f) p.initLink(line, len(llgolink2), f)
} else if strings.HasPrefix(line, llgolink) { } else if strings.HasPrefix(line, llgolink) {
p.initLink(line, len(llgolink), f) p.initLink(line, len(llgolink), f)
} else if strings.HasPrefix(line, exportName) {
p.initCgoExport(line, len(exportName), f)
}
}
func (p *context) initCgoExport(line string, prefix int, f func(inPkgName string) (fullName string, isVar, ok bool)) {
name := strings.TrimSpace(line[prefix:])
if fullName, _, ok := f(name); ok {
p.cgoExports[fullName] = name
} }
} }

View File

@@ -53,7 +53,7 @@ static void* _Cmalloc(size_t size) {
` `
) )
func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string][]string, verbose bool) (cgoLdflags []string, err error) { func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs []string, verbose bool) (cgoLdflags []string, err error) {
cfiles, preambles, cdecls, err := parseCgo_(pkg, files) cfiles, preambles, cdecls, err := parseCgo_(pkg, files)
if err != nil { if err != nil {
return return
@@ -93,8 +93,7 @@ func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string
re := regexp.MustCompile(`^(_cgo_[^_]+_(Cfunc|Cmacro)_)(.*)$`) re := regexp.MustCompile(`^(_cgo_[^_]+_(Cfunc|Cmacro)_)(.*)$`)
cgoSymbols := make(map[string]string) cgoSymbols := make(map[string]string)
mallocFix := false mallocFix := false
for _, symbols := range externs { for _, symbolName := range externs {
for _, symbolName := range symbols {
lastPart := symbolName lastPart := symbolName
lastDot := strings.LastIndex(symbolName, ".") lastDot := strings.LastIndex(symbolName, ".")
if lastDot != -1 { if lastDot != -1 {
@@ -115,7 +114,6 @@ func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string
} }
} }
} }
}
for _, preamble := range preambles { for _, preamble := range preambles {
tmpFile, err := os.CreateTemp("", "-cgo-*.c") tmpFile, err := os.CreateTemp("", "-cgo-*.c")
if err != nil { if err != nil {
@@ -170,10 +168,16 @@ func genExternDeclsByClang(pkg *aPackage, src string, cflags []string, cgoSymbol
var toRemove []string var toRemove []string
for cgoName, symbolName := range cgoSymbols { for cgoName, symbolName := range cgoSymbols {
if strings.HasPrefix(symbolName, "__cgo_") { if strings.HasPrefix(symbolName, "__cgo_") {
cfuncName := symbolName[len("__cgo_"):] gofuncName := strings.Replace(cgoName, ".__cgo_", ".", 1)
cfn := pkg.LPkg.NewFunc(cfuncName, types.NewSignature(nil, nil, nil, false), llssa.InC) gofn := pkg.LPkg.FuncOf(gofuncName)
cgoVar := pkg.LPkg.VarOf(cgoName) cgoVar := pkg.LPkg.VarOf(cgoName)
if gofn != nil {
cgoVar.ReplaceAllUsesWith(gofn.Expr)
} else {
cfuncName := symbolName[len("__cgo_"):]
cfn := pkg.LPkg.NewFunc(cfuncName, types.NewSignatureType(nil, nil, nil, nil, nil, false), llssa.InC)
cgoVar.ReplaceAllUsesWith(cfn.Expr) cgoVar.ReplaceAllUsesWith(cfn.Expr)
}
toRemove = append(toRemove, cgoName) toRemove = append(toRemove, cgoName)
} else { } else {
usePtr := "" usePtr := ""