cgo: support macros
This commit is contained in:
31
_demo/cgomacro/cgomacro.go
Normal file
31
_demo/cgomacro/cgomacro.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo pkg-config: python3-embed
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <Python.h>
|
||||||
|
|
||||||
|
void test_stdout() {
|
||||||
|
printf("stdout ptr: %p\n", stdout);
|
||||||
|
fputs("outputs to stdout\n", stdout);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/c"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
C.test_stdout()
|
||||||
|
C.fputs((*C.char)(unsafe.Pointer(c.Str("hello\n"))), C.stdout)
|
||||||
|
C.Py_Initialize()
|
||||||
|
defer C.Py_Finalize()
|
||||||
|
C.PyObject_Print(C.Py_True, C.stdout, 0)
|
||||||
|
C.fputs((*C.char)(unsafe.Pointer(c.Str("\n"))), C.stdout)
|
||||||
|
C.PyObject_Print(C.Py_False, C.stdout, 0)
|
||||||
|
C.fputs((*C.char)(unsafe.Pointer(c.Str("\n"))), C.stdout)
|
||||||
|
C.PyObject_Print(C.Py_None, C.stdout, 0)
|
||||||
|
C.fputs((*C.char)(unsafe.Pointer(c.Str("\n"))), C.stdout)
|
||||||
|
}
|
||||||
@@ -189,8 +189,17 @@ var (
|
|||||||
argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8]))
|
argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8]))
|
||||||
)
|
)
|
||||||
|
|
||||||
func isCgoCfunc(f *ssa.Function) bool {
|
func isCgoCfuncOrCmacro(f *ssa.Function) bool {
|
||||||
return strings.HasPrefix(f.Name(), "_Cfunc_")
|
name := f.Name()
|
||||||
|
return isCgoCfunc(name) || isCgoCmacro(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCgoCfunc(name string) bool {
|
||||||
|
return strings.HasPrefix(name, "_Cfunc_")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCgoCmacro(name string) bool {
|
||||||
|
return strings.HasPrefix(name, "_Cmacro_")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Function, llssa.PyObjRef, int) {
|
func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Function, llssa.PyObjRef, int) {
|
||||||
@@ -246,10 +255,11 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
|
|||||||
fn.Inline(llssa.NoInline)
|
fn.Inline(llssa.NoInline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isCgo := isCgoCfuncOrCmacro(f)
|
||||||
if nblk := len(f.Blocks); nblk > 0 {
|
if nblk := len(f.Blocks); nblk > 0 {
|
||||||
p.cgoCalled = false
|
p.cgoCalled = false
|
||||||
p.cgoArgs = nil
|
p.cgoArgs = nil
|
||||||
if isCgoCfunc(f) {
|
if isCgo {
|
||||||
fn.MakeBlocks(1)
|
fn.MakeBlocks(1)
|
||||||
} else {
|
} else {
|
||||||
fn.MakeBlocks(nblk) // to set fn.HasBody() = true
|
fn.MakeBlocks(nblk) // to set fn.HasBody() = true
|
||||||
@@ -278,7 +288,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
|
|||||||
}
|
}
|
||||||
p.bvals = make(map[ssa.Value]llssa.Expr)
|
p.bvals = make(map[ssa.Value]llssa.Expr)
|
||||||
off := make([]int, len(f.Blocks))
|
off := make([]int, len(f.Blocks))
|
||||||
if isCgoCfunc(f) {
|
if isCgo {
|
||||||
p.cgoArgs = make([]llssa.Expr, len(f.Params))
|
p.cgoArgs = make([]llssa.Expr, len(f.Params))
|
||||||
for i, param := range f.Params {
|
for i, param := range f.Params {
|
||||||
p.cgoArgs[i] = p.compileValue(b, param)
|
p.cgoArgs[i] = p.compileValue(b, param)
|
||||||
@@ -295,7 +305,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
|
|||||||
doMainInit := (i == 0 && name == "main")
|
doMainInit := (i == 0 && name == "main")
|
||||||
doModInit := (i == 1 && isInit)
|
doModInit := (i == 1 && isInit)
|
||||||
p.compileBlock(b, block, off[i], doMainInit, doModInit)
|
p.compileBlock(b, block, off[i], doMainInit, doModInit)
|
||||||
if isCgoCfunc(f) {
|
if isCgo {
|
||||||
// just process first block for performance
|
// just process first block for performance
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -409,36 +419,46 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
|
|||||||
if funcs, ok := p.cgoFuncs[fname]; ok {
|
if funcs, ok := p.cgoFuncs[fname]; ok {
|
||||||
cgoFuncs = funcs
|
cgoFuncs = funcs
|
||||||
}
|
}
|
||||||
|
fnName := block.Parent().Name()
|
||||||
cgoReturned := false
|
cgoReturned := false
|
||||||
|
isCgoCfunc := isCgoCfunc(fnName)
|
||||||
|
isCgoCmacro := isCgoCmacro(fnName)
|
||||||
for i, instr := range instrs {
|
for i, instr := range instrs {
|
||||||
if i == 1 && doModInit && p.state == pkgInPatch { // in patch package but no pkgFNoOldInit
|
if i == 1 && doModInit && p.state == pkgInPatch { // in patch package but no pkgFNoOldInit
|
||||||
initFnNameOld := initFnNameOfHasPatch(p.fn.Name())
|
initFnNameOld := initFnNameOfHasPatch(p.fn.Name())
|
||||||
fnOld := pkg.NewFunc(initFnNameOld, llssa.NoArgsNoRet, llssa.InC)
|
fnOld := pkg.NewFunc(initFnNameOld, llssa.NoArgsNoRet, llssa.InC)
|
||||||
b.Call(fnOld.Expr)
|
b.Call(fnOld.Expr)
|
||||||
}
|
}
|
||||||
if isCgoCfunc(block.Parent()) {
|
if isCgoCfunc || isCgoCmacro {
|
||||||
switch instr := instr.(type) {
|
switch instr := instr.(type) {
|
||||||
case *ssa.Alloc:
|
case *ssa.Alloc:
|
||||||
// return value allocation
|
// return value allocation
|
||||||
p.compileInstr(b, instr)
|
p.compileInstr(b, instr)
|
||||||
case *ssa.UnOp:
|
case *ssa.UnOp:
|
||||||
// load cgo function pointer
|
// load cgo function pointer
|
||||||
if instr.Op == token.MUL && strings.HasPrefix(instr.X.Name(), "_cgo_") {
|
varName := instr.X.Name()
|
||||||
cgoFuncs = append(cgoFuncs, instr.X.Name())
|
if instr.Op == token.MUL && strings.HasPrefix(varName, "_cgo_") {
|
||||||
|
cgoFuncs = append(cgoFuncs, varName)
|
||||||
p.cgoFuncs[fname] = cgoFuncs
|
p.cgoFuncs[fname] = cgoFuncs
|
||||||
p.compileInstr(b, instr)
|
p.compileInstr(b, instr)
|
||||||
}
|
}
|
||||||
case *ssa.Call:
|
case *ssa.Call:
|
||||||
|
if isCgoCmacro {
|
||||||
|
p.cgoRet = p.compileValue(b, instr.Call.Args[0])
|
||||||
|
p.cgoCalled = true
|
||||||
|
} else {
|
||||||
// call c function
|
// call c function
|
||||||
p.compileInstr(b, instr)
|
p.compileInstr(b, instr)
|
||||||
p.cgoCalled = true
|
p.cgoCalled = true
|
||||||
|
}
|
||||||
case *ssa.Return:
|
case *ssa.Return:
|
||||||
// return cgo function result
|
// return cgo function result
|
||||||
if len(instr.Results) > 0 {
|
if isCgoCmacro {
|
||||||
b.Return(p.cgoRet)
|
ty := p.type_(instr.Results[0].Type(), llssa.InGo)
|
||||||
} else {
|
p.cgoRet.Type = p.prog.Pointer(ty)
|
||||||
b.Return(llssa.Nil)
|
p.cgoRet = b.Load(p.cgoRet)
|
||||||
}
|
}
|
||||||
|
b.Return(p.cgoRet)
|
||||||
cgoReturned = true
|
cgoReturned = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -446,18 +466,14 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// is cgo cfunc but not return yet, some funcs has multiple blocks
|
// is cgo cfunc but not return yet, some funcs has multiple blocks
|
||||||
if isCgoCfunc(block.Parent()) && !cgoReturned {
|
if (isCgoCfunc || isCgoCmacro) && !cgoReturned {
|
||||||
if !p.cgoCalled {
|
if !p.cgoCalled {
|
||||||
panic("cgo cfunc not called")
|
panic("cgo cfunc not called")
|
||||||
}
|
}
|
||||||
for _, block := range block.Parent().Blocks {
|
for _, block := range block.Parent().Blocks {
|
||||||
for _, instr := range block.Instrs {
|
for _, instr := range block.Instrs {
|
||||||
if instr, ok := instr.(*ssa.Return); ok {
|
if _, ok := instr.(*ssa.Return); ok {
|
||||||
if len(instr.Results) > 0 {
|
|
||||||
b.Return(p.cgoRet)
|
b.Return(p.cgoRet)
|
||||||
} else {
|
|
||||||
b.Return(llssa.Nil)
|
|
||||||
}
|
|
||||||
goto end
|
goto end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -421,7 +421,6 @@ const (
|
|||||||
llgoCgoCMalloc = llgoCgoBase + 0x5
|
llgoCgoCMalloc = llgoCgoBase + 0x5
|
||||||
llgoCgoCheckPointer = llgoCgoBase + 0x6
|
llgoCgoCheckPointer = llgoCgoBase + 0x6
|
||||||
llgoCgoCgocall = llgoCgoBase + 0x7
|
llgoCgoCgocall = llgoCgoBase + 0x7
|
||||||
llgoCgoUse = llgoCgoBase + 0x8
|
|
||||||
|
|
||||||
llgoAtomicOpLast = llgoAtomicOpBase + int(llssa.OpUMin)
|
llgoAtomicOpLast = llgoAtomicOpBase + int(llssa.OpUMin)
|
||||||
)
|
)
|
||||||
@@ -438,7 +437,7 @@ func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, strin
|
|||||||
if checkCgo(fname) {
|
if checkCgo(fname) {
|
||||||
return nil, fname, llgoInstr
|
return nil, fname, llgoInstr
|
||||||
}
|
}
|
||||||
if isCgoCfunc(fn) {
|
if isCgoCfuncOrCmacro(fn) {
|
||||||
if _, ok := llgoInstrs[fname]; ok {
|
if _, ok := llgoInstrs[fname]; ok {
|
||||||
return nil, fname, llgoInstr
|
return nil, fname, llgoInstr
|
||||||
}
|
}
|
||||||
@@ -450,7 +449,7 @@ func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, strin
|
|||||||
}
|
}
|
||||||
p.ensureLoaded(pkg)
|
p.ensureLoaded(pkg)
|
||||||
orgName = funcName(pkg, fn, false)
|
orgName = funcName(pkg, fn, false)
|
||||||
if ignore && ignoreName(orgName) || checkCgo(fn.Name()) {
|
if ignore && ignoreName(orgName) {
|
||||||
return nil, orgName, ignoredFunc
|
return nil, orgName, ignoredFunc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,11 +130,6 @@ func (p *context) cgoCgocall(b llssa.Builder, args []ssa.Value) (ret llssa.Expr)
|
|||||||
return p.cgoRet
|
return p.cgoRet
|
||||||
}
|
}
|
||||||
|
|
||||||
// func _Cgo_use(v any)
|
|
||||||
func (p *context) cgoUse(b llssa.Builder, args []ssa.Value) {
|
|
||||||
// don't need to do anything
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// func index(arr *T, idx int) T
|
// func index(arr *T, idx int) T
|
||||||
@@ -324,7 +319,6 @@ var llgoInstrs = map[string]int{
|
|||||||
"_Cfunc__CMalloc": llgoCgoCMalloc,
|
"_Cfunc__CMalloc": llgoCgoCMalloc,
|
||||||
"_cgoCheckPointer": llgoCgoCheckPointer,
|
"_cgoCheckPointer": llgoCgoCheckPointer,
|
||||||
"_cgo_runtime_cgocall": llgoCgoCgocall,
|
"_cgo_runtime_cgocall": llgoCgoCgocall,
|
||||||
"_Cgo_use": llgoCgoUse,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
|
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
|
||||||
@@ -468,8 +462,6 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon
|
|||||||
p.cgoCheckPointer(b, args)
|
p.cgoCheckPointer(b, args)
|
||||||
case llgoCgoCgocall:
|
case llgoCgoCgocall:
|
||||||
p.cgoCgocall(b, args)
|
p.cgoCgocall(b, args)
|
||||||
case llgoCgoUse:
|
|
||||||
p.cgoUse(b, args)
|
|
||||||
case llgoAdvance:
|
case llgoAdvance:
|
||||||
ret = p.advance(b, args)
|
ret = p.advance(b, args)
|
||||||
case llgoIndex:
|
case llgoIndex:
|
||||||
|
|||||||
@@ -88,18 +88,18 @@ func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string
|
|||||||
cgoLdflags = append(cgoLdflags, linkFile)
|
cgoLdflags = append(cgoLdflags, linkFile)
|
||||||
}, verbose)
|
}, verbose)
|
||||||
}
|
}
|
||||||
re := regexp.MustCompile(`^(_cgo_[^_]+_Cfunc_)(.*)$`)
|
re := regexp.MustCompile(`^(_cgo_[^_]+_(Cfunc|Cmacro)_)(.*)$`)
|
||||||
cgoFuncs := make(map[string]string)
|
cgoSymbols := make(map[string]string)
|
||||||
mallocFix := false
|
mallocFix := false
|
||||||
for _, funcs := range externs {
|
for _, symbols := range externs {
|
||||||
for _, funcName := range funcs {
|
for _, symbolName := range symbols {
|
||||||
if m := re.FindStringSubmatch(funcName); len(m) > 0 {
|
if m := re.FindStringSubmatch(symbolName); len(m) > 0 {
|
||||||
cgoFuncs[funcName] = m[2]
|
cgoSymbols[symbolName] = m[3]
|
||||||
// fix missing _cgo_9113e32b6599_Cfunc__Cmalloc
|
|
||||||
if !mallocFix {
|
|
||||||
pkgPrefix := m[1]
|
pkgPrefix := m[1]
|
||||||
|
// fix missing _cgo_9113e32b6599_Cfunc__Cmalloc
|
||||||
|
if !mallocFix && m[2] == "Cfunc" {
|
||||||
mallocName := pkgPrefix + "_Cmalloc"
|
mallocName := pkgPrefix + "_Cmalloc"
|
||||||
cgoFuncs[mallocName] = "_Cmalloc"
|
cgoSymbols[mallocName] = "_Cmalloc"
|
||||||
mallocFix = true
|
mallocFix = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,10 @@ func buildCgo(ctx *context, pkg *aPackage, files []*ast.File, externs map[string
|
|||||||
tmpName := tmpFile.Name()
|
tmpName := tmpFile.Name()
|
||||||
defer os.Remove(tmpName)
|
defer os.Remove(tmpName)
|
||||||
code := cgoHeader + "\n\n" + preamble.src
|
code := cgoHeader + "\n\n" + preamble.src
|
||||||
externDecls := genExternDeclsByClang(code, cflags, cgoFuncs)
|
externDecls, err := genExternDeclsByClang(code, cflags, cgoSymbols)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if err = os.WriteFile(tmpName, []byte(code+"\n\n"+externDecls), 0644); err != nil {
|
if err = os.WriteFile(tmpName, []byte(code+"\n\n"+externDecls), 0644); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -134,42 +137,90 @@ type clangASTNode struct {
|
|||||||
Inner []clangASTNode `json:"inner,omitempty"`
|
Inner []clangASTNode `json:"inner,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func genExternDeclsByClang(src string, cflags []string, cgoFuncs map[string]string) string {
|
func genExternDeclsByClang(src string, cflags []string, cgoSymbols map[string]string) (string, error) {
|
||||||
tmpSrc, err := os.CreateTemp("", "cgo-src-*.c")
|
tmpSrc, err := os.CreateTemp("", "cgo-src-*.c")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return "", err
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpSrc.Name())
|
defer os.Remove(tmpSrc.Name())
|
||||||
if err := os.WriteFile(tmpSrc.Name(), []byte(src), 0644); err != nil {
|
if err := os.WriteFile(tmpSrc.Name(), []byte(src), 0644); err != nil {
|
||||||
return ""
|
return "", err
|
||||||
}
|
}
|
||||||
args := append([]string{"-Xclang", "-ast-dump=json", "-fsyntax-only"}, cflags...)
|
symbolNames := make(map[string]bool)
|
||||||
args = append(args, tmpSrc.Name())
|
if err := getFuncNames(tmpSrc.Name(), cflags, symbolNames); err != nil {
|
||||||
cmd := exec.Command("clang", args...)
|
return "", err
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
var astRoot clangASTNode
|
macroNames := make(map[string]bool)
|
||||||
if err := json.Unmarshal(output, &astRoot); err != nil {
|
if err := getMacroNames(tmpSrc.Name(), cflags, macroNames); err != nil {
|
||||||
return ""
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
funcNames := make(map[string]bool)
|
|
||||||
extractFuncNames(&astRoot, funcNames)
|
|
||||||
|
|
||||||
b := strings.Builder{}
|
b := strings.Builder{}
|
||||||
var toRemove []string
|
var toRemove []string
|
||||||
for cgoFunc, funcName := range cgoFuncs {
|
for cgoName, symbolName := range cgoSymbols {
|
||||||
if funcNames[funcName] {
|
if symbolNames[symbolName] {
|
||||||
b.WriteString(fmt.Sprintf("void* %s = (void*)%s;\n", cgoFunc, funcName))
|
b.WriteString(fmt.Sprintf("void* %s = (void*)%s;\n", cgoName, symbolName))
|
||||||
toRemove = append(toRemove, cgoFunc)
|
toRemove = append(toRemove, cgoName)
|
||||||
|
} else if macroNames[symbolName] {
|
||||||
|
/* template:
|
||||||
|
typeof(stdout) _cgo_1574167f3838_Cmacro_stdout;
|
||||||
|
|
||||||
|
__attribute__((constructor))
|
||||||
|
static void _init__cgo_1574167f3838_Cmacro_stdout() {
|
||||||
|
_cgo_1574167f3838_Cmacro_stdout = stdout;
|
||||||
|
}*/
|
||||||
|
b.WriteString(fmt.Sprintf(`
|
||||||
|
typeof(%s) %s;
|
||||||
|
|
||||||
|
__attribute__((constructor))
|
||||||
|
static void _init_%s() {
|
||||||
|
%s = %s;
|
||||||
|
}
|
||||||
|
`, symbolName, cgoName, cgoName, cgoName, symbolName))
|
||||||
|
toRemove = append(toRemove, cgoName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, funcName := range toRemove {
|
for _, funcName := range toRemove {
|
||||||
delete(cgoFuncs, funcName)
|
delete(cgoSymbols, funcName)
|
||||||
}
|
}
|
||||||
return b.String()
|
return b.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMacroNames(file string, cflags []string, macroNames map[string]bool) error {
|
||||||
|
args := append([]string{"-dM", "-E"}, cflags...)
|
||||||
|
args = append(args, file)
|
||||||
|
cmd := exec.Command("clang", args...)
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, line := range strings.Split(string(output), "\n") {
|
||||||
|
if strings.HasPrefix(line, "#define ") {
|
||||||
|
define := strings.TrimPrefix(line, "#define ")
|
||||||
|
parts := strings.SplitN(define, " ", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
macroNames[parts[0]] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFuncNames(file string, cflags []string, symbolNames map[string]bool) error {
|
||||||
|
args := append([]string{"-Xclang", "-ast-dump=json", "-fsyntax-only"}, cflags...)
|
||||||
|
args = append(args, file)
|
||||||
|
cmd := exec.Command("clang", args...)
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var astRoot clangASTNode
|
||||||
|
if err := json.Unmarshal(output, &astRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
extractFuncNames(&astRoot, symbolNames)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractFuncNames(node *clangASTNode, funcNames map[string]bool) {
|
func extractFuncNames(node *clangASTNode, funcNames map[string]bool) {
|
||||||
|
|||||||
Reference in New Issue
Block a user