cgo: full supports cgo preambles and auto compile c files

This commit is contained in:
Li Jie
2024-11-14 16:13:13 +08:00
parent 89b111edca
commit a64f4219e9
16 changed files with 588 additions and 53 deletions

View File

@@ -114,6 +114,11 @@ type context struct {
state pkgState
inCFunc bool
skipall bool
cgoCalled bool
cgoArgs []llssa.Expr
cgoRet llssa.Expr
cgoFuncs map[string][]string
}
type pkgState byte
@@ -163,7 +168,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
typ := globalType(gbl)
name, vtype, define := p.varName(gbl.Pkg.Pkg, gbl)
if vtype == pyVar || ignoreName(name) || checkCgo(name) {
if vtype == pyVar || ignoreName(name) {
return
}
if debugInstr {
@@ -189,6 +194,10 @@ var (
argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8]))
)
func isCgoCfunc(f *ssa.Function) bool {
return strings.HasPrefix(f.Name(), "_Cfunc_")
}
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 {
@@ -242,9 +251,14 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
fn.Inline(llssa.NoInline)
}
}
if nblk := len(f.Blocks); nblk > 0 {
fn.MakeBlocks(nblk) // to set fn.HasBody() = true
p.cgoCalled = false
p.cgoArgs = nil
if isCgoCfunc(f) {
fn.MakeBlocks(1)
} else {
fn.MakeBlocks(nblk) // to set fn.HasBody() = true
}
if f.Recover != nil { // set recover block
fn.SetRecover(fn.Block(f.Recover.Index))
}
@@ -269,8 +283,15 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
}
p.bvals = make(map[ssa.Value]llssa.Expr)
off := make([]int, len(f.Blocks))
for i, block := range f.Blocks {
off[i] = p.compilePhis(b, block)
if isCgoCfunc(f) {
p.cgoArgs = make([]llssa.Expr, len(f.Params))
for i, param := range f.Params {
p.cgoArgs[i] = p.compileValue(b, param)
}
} else {
for i, block := range f.Blocks {
off[i] = p.compilePhis(b, block)
}
}
p.blkInfos = blocks.Infos(f.Blocks)
i := 0
@@ -279,6 +300,10 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
doMainInit := (i == 0 && name == "main")
doModInit := (i == 1 && isInit)
p.compileBlock(b, block, off[i], doMainInit, doModInit)
if isCgoCfunc(f) {
// just process first block for performance
break
}
if i = p.blkInfos[i].Next; i < 0 {
break
}
@@ -381,14 +406,69 @@ 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
}
cgoReturned := false
for i, instr := range instrs {
if i == 1 && doModInit && p.state == pkgInPatch { // in patch package but no pkgFNoOldInit
initFnNameOld := initFnNameOfHasPatch(p.fn.Name())
fnOld := pkg.NewFunc(initFnNameOld, llssa.NoArgsNoRet, llssa.InC)
b.Call(fnOld.Expr)
}
p.compileInstr(b, instr)
if isCgoCfunc(block.Parent()) {
switch instr := instr.(type) {
case *ssa.Alloc:
// return value allocation
p.compileInstr(b, instr)
case *ssa.UnOp:
// load cgo function pointer
if instr.Op == token.MUL && strings.HasPrefix(instr.X.Name(), "_cgo_") {
cgoFuncs = append(cgoFuncs, instr.X.Name())
p.cgoFuncs[fname] = cgoFuncs
p.compileInstr(b, instr)
}
case *ssa.Call:
// call c function
p.compileInstr(b, instr)
p.cgoCalled = true
case *ssa.Return:
// return cgo function result
if len(instr.Results) > 0 {
b.Return(p.cgoRet)
} else {
b.Return(llssa.Nil)
}
cgoReturned = true
}
} else {
p.compileInstr(b, instr)
}
}
// is cgo cfunc but not return yet, some funcs has multiple blocks
if isCgoCfunc(block.Parent()) && !cgoReturned {
if !p.cgoCalled {
panic("cgo cfunc not called")
}
for _, block := range block.Parent().Blocks {
for _, instr := range block.Instrs {
if instr, ok := instr.(*ssa.Return); ok {
if len(instr.Results) > 0 {
b.Return(p.cgoRet)
} else {
b.Return(llssa.Nil)
}
goto end
}
}
}
}
end:
if pyModInit {
jump := block.Instrs[n+last].(*ssa.Jump)
jumpTo := p.jumpTo(jump)
@@ -721,7 +801,7 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
case *ssa.Store:
// skip cgo global variables
if checkCgo(v.Addr.Name()) {
return
// return
}
va := v.Addr
if va, ok := va.(*ssa.IndexAddr); ok {
@@ -896,11 +976,12 @@ type Patches = map[string]Patch
// NewPackage compiles a Go package to LLVM IR package.
func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, err error) {
return NewPackageEx(prog, nil, pkg, files)
ret, _, err = NewPackageEx(prog, nil, pkg, files)
return
}
// 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, err error) {
func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs map[string][]string, err error) {
pkgProg := pkg.Prog
pkgTypes := pkg.Pkg
oldTypes := pkgTypes
@@ -964,6 +1045,7 @@ func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files [
ctx.initAfter = nil
fn()
}
externs = ctx.cgoFuncs
return
}
@@ -1003,7 +1085,7 @@ func processPkg(ctx *context, ret llssa.Package, pkg *ssa.Package) {
case *ssa.Global:
// skip cgo global variables
if checkCgo(member.Name()) {
continue
// continue
}
ctx.compileGlobal(ret, member)
}