diff --git a/cl/_testpy/callpy/in.go b/cl/_testpy/callpy/in.go new file mode 100644 index 00000000..21ec21ab --- /dev/null +++ b/cl/_testpy/callpy/in.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/py" + "github.com/goplus/llgo/py/math" +) + +func main() { + x := math.Sqrt(py.Float(2)) + c.Printf(c.Str("sqrt(2) = %f\n"), x.Float64()) +} diff --git a/cl/_testpy/callpy/out.ll b/cl/_testpy/callpy/out.ll new file mode 100644 index 00000000..e69de29b diff --git a/cl/compile.go b/cl/compile.go index 21dae4a1..de93ed5c 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -211,19 +211,19 @@ var ( argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8])) ) -func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) llssa.Function { +func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Function, llssa.PyFunction, int) { pkgTypes, name, ftype := p.funcName(f, true) if ftype != goFunc { if ftype == pyFunc { // TODO(xsw): pyMod == "" - fn := pysymPrefix + p.pyMod + "." + name - pkg.NewVar(fn, pkg.Prog.PyObjectPtrPtr().RawType(), llssa.InC) + fnName := pysymPrefix + p.pyMod + "." + name + return nil, pkg.NewPyFunc(fnName, f.Signature), pyFunc } - return nil + return nil, nil, ignoredFunc } fn := pkg.FuncOf(name) if fn != nil && fn.HasBody() { - return fn + return fn, nil, goFunc } var sig = f.Signature @@ -279,14 +279,20 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) llssa.Func } }) } - return fn + return fn, nil, goFunc } // funcOf returns a function by name and set ftype = goFunc, cFunc, etc. // or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc. -func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) { +func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyFunction, ftype int) { _, name, ftype := p.funcName(fn, false) - if ftype == llgoInstr { + switch ftype { + case pyFunc: + pkg := p.pkg + if pyFn = pkg.PyFuncOf(name); pyFn == nil { + pyFn = pkg.NewPyFunc(name, fn.Signature) + } + case llgoInstr: switch name { case "cstr": ftype = llgoCstr @@ -307,11 +313,14 @@ func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) { default: panic("unknown llgo instruction: " + name) } - } else { + default: pkg := p.pkg - if ret = pkg.FuncOf(name); ret == nil && len(fn.FreeVars) == 0 { + if aFn = pkg.FuncOf(name); aFn == nil { + if len(fn.FreeVars) > 0 { + return nil, nil, ignoredFunc + } sig := fn.Signature - ret = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false) + aFn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false) } } return @@ -542,11 +551,13 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue ret = b.BuiltinCall(fn, args...) } case *ssa.Function: - fn, ftype := p.compileFunction(cv) + aFn, pyFn, ftype := p.compileFunction(cv) switch ftype { case goFunc, cFunc: args := p.compileValues(b, args, kind) - ret = b.Call(fn.Expr, args...) + ret = b.Call(aFn.Expr, args...) + case pyFunc: + log.Panicln("pyFunc:", pyFn) case llgoCstr: ret = cstr(b, args) case llgoAdvance: @@ -742,12 +753,13 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { } } -func (p *context) compileFunction(v *ssa.Function) (llssa.Function, int) { +func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyFunction, kind int) { // v.Pkg == nil: means auto generated function? if v.Pkg == p.goPkg || v.Pkg == nil { // function in this package - if fn := p.compileFuncDecl(p.pkg, v); fn != nil { - return fn, goFunc + goFn, pyFn, kind = p.compileFuncDecl(p.pkg, v) + if kind != ignoredFunc { + return } } return p.funcOf(v) @@ -766,8 +778,11 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { } } case *ssa.Function: - fn, _ := p.compileFunction(v) - return fn.Expr + aFn, pyFn, _ := p.compileFunction(v) + if aFn != nil { + return aFn.Expr + } + return pyFn.Expr case *ssa.Global: g := p.varOf(v) return g.Expr diff --git a/cl/compile_test.go b/cl/compile_test.go index 1af95938..e95916db 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -29,7 +29,7 @@ func testCompile(t *testing.T, src, expected string) { } func TestFromTestpy(t *testing.T) { - cltest.FromDir(t, "", "./_testpy", false) + cltest.FromDir(t, "callpy", "./_testpy", false) } func TestFromTestlibc(t *testing.T) { diff --git a/cl/import.go b/cl/import.go index 24e1ac74..fe70af9a 100644 --- a/cl/import.go +++ b/cl/import.go @@ -345,7 +345,7 @@ func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, strin return nil, v[2:], cFunc } if strings.HasPrefix(v, "py.") { - return nil, v[3:], pyFunc + return pkg, v[3:], pyFunc } if strings.HasPrefix(v, "llgo.") { return nil, v[5:], llgoInstr diff --git a/ssa/decl.go b/ssa/decl.go index 72b480c9..b89243fa 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -18,6 +18,7 @@ package ssa import ( "go/types" + "log" "strconv" "github.com/goplus/llvm" @@ -68,6 +69,23 @@ type aGlobal struct { // variable. type Global = *aGlobal +// NewVar creates a new global variable. +func (p Package) NewVar(name string, typ types.Type, bg Background) Global { + if v, ok := p.vars[name]; ok { + return v + } + t := p.Prog.Type(typ, bg) + gbl := llvm.AddGlobal(p.mod, t.ll, name) + ret := &aGlobal{Expr{gbl, t}} + p.vars[name] = ret + return ret +} + +// VarOf returns a global variable by name. +func (p Package) VarOf(name string) Global { + return p.vars[name] +} + // Init initializes the global variable with the given value. func (g Global) Init(v Expr) { g.impl.SetInitializer(v.impl) @@ -140,6 +158,31 @@ type aFunction struct { // Function represents a function or method. type Function = *aFunction +// NewFunc creates a new function. +func (p Package) NewFunc(name string, sig *types.Signature, bg Background) Function { + return p.NewFuncEx(name, sig, bg, false) +} + +// NewFuncEx creates a new function. +func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, hasFreeVars bool) Function { + if v, ok := p.fns[name]; ok { + return v + } + t := p.Prog.FuncDecl(sig, bg) + if debugInstr { + log.Println("NewFunc", name, t.raw.Type, "hasFreeVars:", hasFreeVars) + } + fn := llvm.AddFunction(p.mod, name, t.ll) + ret := newFunction(fn, t, p, p.Prog, hasFreeVars) + p.fns[name] = ret + return ret +} + +// FuncOf returns a function by name. +func (p Package) FuncOf(name string) Function { + return p.fns[name] +} + func newFunction(fn llvm.Value, t Type, pkg Package, prog Program, hasFreeVars bool) Function { params, hasVArg := newParams(t, prog) base := 0 @@ -241,3 +284,31 @@ func (p Function) Block(idx int) BasicBlock { } // ----------------------------------------------------------------------------- + +type aPyFunction struct { + Expr + Obj Global +} + +// PyFunction represents a python function. +type PyFunction = *aPyFunction + +// NewPyFunc creates a new python function. +func (p Package) NewPyFunc(name string, sig *types.Signature) PyFunction { + if v, ok := p.pyfns[name]; ok { + return v + } + obj := p.NewVar(name, p.Prog.PyObjectPtrPtr().RawType(), InC) + ty := &aType{obj.ll, rawType{sig}, vkPyFunc} + expr := Expr{obj.impl, ty} + ret := &aPyFunction{expr, obj} + p.pyfns[name] = ret + return ret +} + +// PyFuncOf returns a function by name. +func (p Package) PyFuncOf(name string) PyFunction { + return p.pyfns[name] +} + +// ----------------------------------------------------------------------------- diff --git a/ssa/package.go b/ssa/package.go index a9f5b2ef..2f5decdc 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -19,7 +19,6 @@ package ssa import ( "go/token" "go/types" - "log" "github.com/goplus/llvm" "golang.org/x/tools/go/types/typeutil" @@ -260,8 +259,9 @@ func (p Program) NewPackage(name, pkgPath string) Package { gbls := make(map[string]Global) fns := make(map[string]Function) stubs := make(map[string]Function) + pyfns := make(map[string]PyFunction) p.needRuntime = false - return &aPackage{mod, gbls, fns, stubs, p} + return &aPackage{mod, gbls, fns, stubs, pyfns, p} } // PyObjectPtrPtr returns the **py.Object type. @@ -365,6 +365,7 @@ type aPackage struct { vars map[string]Global fns map[string]Function stubs map[string]Function + pyfns map[string]PyFunction Prog Program } @@ -377,40 +378,6 @@ func (p Package) NewConst(name string, val constant.Value) NamedConst { } */ -// NewVar creates a new global variable. -func (p Package) NewVar(name string, typ types.Type, bg Background) Global { - t := p.Prog.Type(typ, bg) - gbl := llvm.AddGlobal(p.mod, t.ll, name) - ret := &aGlobal{Expr{gbl, t}} - p.vars[name] = ret - return ret -} - -// VarOf returns a global variable by name. -func (p Package) VarOf(name string) Global { - return p.vars[name] -} - -// NewFunc creates a new function. -func (p Package) NewFunc(name string, sig *types.Signature, bg Background) Function { - return p.NewFuncEx(name, sig, bg, false) -} - -// NewFuncEx creates a new function. -func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, hasFreeVars bool) Function { - if v, ok := p.fns[name]; ok { - return v - } - t := p.Prog.FuncDecl(sig, bg) - if debugInstr { - log.Println("NewFunc", name, t.raw.Type, "hasFreeVars:", hasFreeVars) - } - fn := llvm.AddFunction(p.mod, name, t.ll) - ret := newFunction(fn, t, p, p.Prog, hasFreeVars) - p.fns[name] = ret - return ret -} - func (p Package) rtFunc(fnName string) Expr { fn := p.Prog.runtime().Scope().Lookup(fnName).(*types.Func) name := FullName(fn.Pkg(), fnName) @@ -456,11 +423,6 @@ func (p Package) closureStub(b Builder, t *types.Struct, v Expr) Expr { return b.aggregateValue(prog.rawType(t), v.impl, nilVal) } -// FuncOf returns a function by name. -func (p Package) FuncOf(name string) Function { - return p.fns[name] -} - // ----------------------------------------------------------------------------- // String returns a string representation of the package. @@ -521,6 +483,13 @@ func (p Program) tyImportPyModule() *types.Signature { return p.pyImpTy } +// ImportPyMod imports a Python module. +func (b Builder) ImportPyMod(path string) Expr { + pkg := b.Func.Pkg + fnImp := pkg.cpyFunc("PyImport_ImportModule", b.Prog.tyImportPyModule()) + return b.Call(fnImp, b.CStr(path)) +} + // NewPyModVar creates a new global variable for a Python module. func (p Package) NewPyModVar(name string) Global { prog := p.Prog @@ -531,11 +500,4 @@ func (p Package) NewPyModVar(name string) Global { return g } -// ImportPyMod imports a Python module. -func (b Builder) ImportPyMod(path string) Expr { - pkg := b.Func.Pkg - fnImp := pkg.cpyFunc("PyImport_ImportModule", b.Prog.tyImportPyModule()) - return b.Call(fnImp, b.CStr(path)) -} - // ----------------------------------------------------------------------------- diff --git a/ssa/type.go b/ssa/type.go index aecad47d..b1abc561 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -43,6 +43,7 @@ const ( vkFuncDecl vkFuncPtr vkClosure + vkPyFunc vkTuple vkPhisExpr = -1 )