Skip to content

Commit

Permalink
cgo: supports //export functions only calls in C
Browse files Browse the repository at this point in the history
  • Loading branch information
cpunion committed Nov 27, 2024
1 parent e46b3e2 commit c3407ea
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 68 deletions.
8 changes: 8 additions & 0 deletions cl/_testgo/cgofull/cgofull.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down
56 changes: 15 additions & 41 deletions cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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:
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
16 changes: 13 additions & 3 deletions cl/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,16 +246,26 @@ 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)
} else if strings.HasPrefix(line, llgolink2) {
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
}
}

Expand Down
52 changes: 28 additions & 24 deletions internal/build/cgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
}
Expand Down Expand Up @@ -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 := ""
Expand Down

0 comments on commit c3407ea

Please sign in to comment.