diff --git a/cl/_testgo/cgofull/cgofull.go b/cl/_testgo/cgofull/cgofull.go index 8315e20e8..7d5fa45cd 100644 --- a/cl/_testgo/cgofull/cgofull.go +++ b/cl/_testgo/cgofull/cgofull.go @@ -94,8 +94,11 @@ static void test_callback(Cb cb) { printf("done\n"); } +extern int go_callback_not_use_in_go(int); + static void run_callback() { test_callback(c_callback); + test_callback(go_callback_not_use_in_go); } */ import "C" @@ -107,6 +110,11 @@ import ( "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 func go_callback(i C.int) C.int { return i + 1 diff --git a/cl/compile.go b/cl/compile.go index eb4f1fcbf..62441e69e 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -110,10 +110,11 @@ type context struct { inCFunc bool skipall bool - cgoCalled bool - cgoArgs []llssa.Expr - cgoRet llssa.Expr - cgoFuncs map[string][]string + cgoCalled bool + cgoArgs []llssa.Expr + cgoRet llssa.Expr + cgoSymbols []string + cgoExports map[string]string } type pkgState byte @@ -415,14 +416,6 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do callRuntimeInit(b, pkg) 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() cgoReturned := false isCgoCfunc := isCgoCfunc(fnName) @@ -442,8 +435,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do // load cgo function pointer varName := instr.X.Name() if instr.Op == token.MUL && strings.HasPrefix(varName, "_cgo_") { - cgoFuncs = append(cgoFuncs, varName) - p.cgoFuncs[fname] = cgoFuncs + p.cgoSymbols = append(p.cgoSymbols, varName) p.compileInstr(b, instr) } case *ssa.Call: @@ -925,13 +917,7 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { 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 + p.cgoSymbols = append(p.cgoSymbols, val.Name()) } if debugSymbols { 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. -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 pkgTypes := pkg.Pkg oldTypes := pkgTypes @@ -1033,6 +1019,8 @@ func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files [ loaded: map[*types.Package]*pkgInfo{ types.Unsafe: {kind: PkgDeclOnly}, // TODO(xsw): PkgNoInit or PkgDeclOnly? }, + cgoExports: make(map[string]string), + cgoSymbols: make([]string, 0, 128), } ctx.initPyModule() ctx.initFiles(pkgPath, files) @@ -1066,25 +1054,11 @@ func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files [ ctx.initAfter = nil 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) - } - } + externs = ctx.cgoSymbols + for fnName, exportName := range ctx.cgoExports { + fn := ret.FuncOf(fnName) + if fn != nil { + fn.SetName(exportName) } } return diff --git a/cl/import.go b/cl/import.go index 643950b91..8e163e9a4 100644 --- a/cl/import.go +++ b/cl/import.go @@ -246,9 +246,10 @@ func (p *context) initLinknameByDoc(doc *ast.CommentGroup, fullName, inPkgName s func (p *context) initLinkname(line string, f func(inPkgName string) (fullName string, isVar, ok bool)) { const ( - linkname = "//go:linkname " - llgolink = "//llgo:link " - llgolink2 = "// llgo:link " + linkname = "//go:linkname " + llgolink = "//llgo:link " + llgolink2 = "// llgo:link " + exportName = "//export " ) if strings.HasPrefix(line, linkname) { 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) } else if strings.HasPrefix(line, llgolink) { 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 } } diff --git a/internal/build/cgo.go b/internal/build/cgo.go index ac2b31e81..f53a6deee 100644 --- a/internal/build/cgo.go +++ b/internal/build/cgo.go @@ -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) if err != nil { return @@ -93,26 +93,24 @@ func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string re := regexp.MustCompile(`^(_cgo_[^_]+_(Cfunc|Cmacro)_)(.*)$`) cgoSymbols := make(map[string]string) mallocFix := false - for _, symbols := range externs { - for _, symbolName := range symbols { - 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 := prefix + "_Cmalloc" - cgoSymbols[mallocName] = "_Cmalloc" - mallocFix = true - } + for _, symbolName := range externs { + 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 := prefix + "_Cmalloc" + cgoSymbols[mallocName] = "_Cmalloc" + mallocFix = true } } } @@ -170,10 +168,16 @@ func genExternDeclsByClang(pkg *aPackage, src string, cflags []string, cgoSymbol var toRemove []string for cgoName, symbolName := range cgoSymbols { if strings.HasPrefix(symbolName, "__cgo_") { - cfuncName := symbolName[len("__cgo_"):] - cfn := pkg.LPkg.NewFunc(cfuncName, types.NewSignature(nil, nil, nil, false), llssa.InC) + gofuncName := strings.Replace(cgoName, ".__cgo_", ".", 1) + gofn := pkg.LPkg.FuncOf(gofuncName) cgoVar := pkg.LPkg.VarOf(cgoName) - cgoVar.ReplaceAllUsesWith(cfn.Expr) + 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) + } toRemove = append(toRemove, cgoName) } else { usePtr := ""