diff --git a/README.md b/README.md index 11930a20..53e6b3f9 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ See [github.com/goplus/llgo/c](https://pkg.go.dev/github.com/goplus/llgo/c) for You can import a Python library in `llgo`! -And `llgo` can import any Python library into `llgo` through a program called `llpyg`. The currently imported packages include: +And you can import any Python library into `llgo` through a program called `llpyg`. The currently imported packages include: * [sys](https://pkg.go.dev/github.com/goplus/llgo/py/sys) * [os](https://pkg.go.dev/github.com/goplus/llgo/py/os) diff --git a/_pydemo/matrix/matrix.go b/_pydemo/matrix/matrix.go index ceecb933..5567d172 100644 --- a/_pydemo/matrix/matrix.go +++ b/_pydemo/matrix/matrix.go @@ -18,7 +18,5 @@ func main() { py.List(3.0, 2.0, 1.0), ) x := numpy.Add(a, b) - c.Printf(c.Str("a = %s\n"), a.Str().CStr()) - c.Printf(c.Str("a = %s\n"), b.Str().CStr()) c.Printf(c.Str("a+b = %s\n"), x.Str().CStr()) } diff --git a/_pydemo/pi/pi.go b/_pydemo/pi/pi.go index 934fe31a..239bc543 100644 --- a/_pydemo/pi/pi.go +++ b/_pydemo/pi/pi.go @@ -6,5 +6,5 @@ import ( ) func main() { - c.Printf(c.Str("pi = %f\n"), math.Pi) + c.Printf(c.Str("pi = %f\n"), math.Pi.Float64()) } diff --git a/cl/_testpy/pi/in.go b/cl/_testpy/pi/in.go new file mode 100644 index 00000000..239bc543 --- /dev/null +++ b/cl/_testpy/pi/in.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/py/math" +) + +func main() { + c.Printf(c.Str("pi = %f\n"), math.Pi.Float64()) +} diff --git a/cl/_testpy/pi/out.ll b/cl/_testpy/pi/out.ll new file mode 100644 index 00000000..696cb264 --- /dev/null +++ b/cl/_testpy/pi/out.ll @@ -0,0 +1,49 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = global ptr null +@0 = private unnamed_addr constant [9 x i8] c"pi = %f\0A\00", align 1 +@__llgo_py.math = external global ptr +@1 = private unnamed_addr constant [3 x i8] c"pi\00", align 1 + +define void @main.init() { +_llgo_0: + %0 = load i1, ptr @"main.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"main.init$guard", align 1 + call void @"github.com/goplus/llgo/py/math.init"() + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main(i32 %0, ptr %1) { +_llgo_0: + call void @Py_Initialize() + store i32 %0, ptr @__llgo_argc, align 4 + store ptr %1, ptr @__llgo_argv, align 8 + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %2 = load ptr, ptr @__llgo_py.math, align 8 + %3 = call ptr @PyObject_GetAttrString(ptr %2, ptr @1) + %4 = call double @PyFloat_AsDouble(ptr %3) + %5 = call i32 (ptr, ...) @printf(ptr @0, double %4) + ret void +} + +declare void @"github.com/goplus/llgo/py/math.init"() + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @PyObject_GetAttrString(ptr, ptr) + +declare double @PyFloat_AsDouble(ptr) + +declare i32 @printf(ptr, ...) + +declare void @Py_Initialize() diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 30841981..160aa1b3 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -226,5 +226,5 @@ func TestErrVarOf(t *testing.T) { } ssaPkg := &ssa.Package{Pkg: pkgTypes} g := &ssa.Global{Pkg: ssaPkg} - ctx.varOf(g) + ctx.varOf(nil, g) } diff --git a/cl/compile.go b/cl/compile.go index 43d88f9f..606347a2 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -176,7 +176,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) { for i, n := 0, mthds.Len(); i < n; i++ { mthd := mthds.At(i) if ssaMthd := prog.MethodValue(mthd); ssaMthd != nil { - p.compileFuncDecl(pkg, ssaMthd, false) + p.compileFuncDecl(pkg, ssaMthd) } } } @@ -185,7 +185,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) { func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { typ := gbl.Type() name, vtype := p.varName(gbl.Pkg.Pkg, gbl) - if ignoreName(name) || checkCgo(gbl.Name()) { + if vtype == pyVar || ignoreName(name) || checkCgo(gbl.Name()) { return } if debugInstr { @@ -211,7 +211,7 @@ var ( argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8])) ) -func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function, call bool) (llssa.Function, llssa.PyObjRef, int) { +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 { /* @@ -221,7 +221,6 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function, call bool) return nil, pkg.NewPyFunc(fnName, f.Signature, call), pyFunc } */ - _ = call return nil, nil, ignoredFunc } fn := pkg.FuncOf(name) @@ -294,7 +293,7 @@ func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObj pkg := p.pkg fnName := pysymPrefix + mod + "." + name if pyFn = pkg.PyObjOf(fnName); pyFn == nil { - pyFn = pkg.NewPyFunc(fnName, fn.Signature, true) + pyFn = pkg.PyNewFunc(fnName, fn.Signature, true) return } } @@ -372,7 +371,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do b.SetBlockEx(ret, llssa.AfterInit) for _, modName := range modNames { objs := mods[modName] - b.LoadPyModSyms(modName, objs...) + b.PyLoadModSyms(modName, objs...) } } }) @@ -396,13 +395,13 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do jumpTo := p.jumpTo(jump) modPath := p.pyMod modName := pysymPrefix + modPath - modPtr := pkg.NewPyModVar(modName, true).Expr + modPtr := pkg.PyNewModVar(modName, true).Expr mod := b.Load(modPtr) cond := b.BinOp(token.NEQ, mod, prog.Null(mod.Type)) newBlk := p.fn.MakeBlock() b.If(cond, jumpTo, newBlk) b.SetBlock(newBlk) - b.Store(modPtr, b.ImportPyMod(modPath)) + b.Store(modPtr, b.PyImportMod(modPath)) b.Jump(jumpTo) } return ret @@ -805,7 +804,7 @@ func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn ll // v.Pkg == nil: means auto generated function? if v.Pkg == p.goPkg || v.Pkg == nil { // function in this package - goFn, pyFn, kind = p.compileFuncDecl(p.pkg, v, true) + goFn, pyFn, kind = p.compileFuncDecl(p.pkg, v) if kind != ignoredFunc { return } @@ -832,8 +831,7 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { } return pyFn.Expr case *ssa.Global: - g := p.varOf(v) - return g.Expr + return p.varOf(b, v) case *ssa.Const: t := types.Default(v.Type()) return b.Const(v.Value, p.prog.Type(t, llssa.InGo)) @@ -924,7 +922,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll // Do not try to build generic (non-instantiated) functions. continue } - ctx.compileFuncDecl(ret, member, false) + ctx.compileFuncDecl(ret, member) case *ssa.Type: ctx.compileType(ret, member) case *ssa.Global: diff --git a/cl/import.go b/cl/import.go index 1ab5baea..f59f0ac6 100644 --- a/cl/import.go +++ b/cl/import.go @@ -359,24 +359,35 @@ const ( ignoredVar = iota goVar = int(llssa.InGo) cVar = int(llssa.InC) + pyVar = int(llssa.InPython) ) func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, vtype int) { name := llssa.FullName(pkg, v.Name()) if v, ok := p.link[name]; ok { + if strings.HasPrefix(v, "py.") { + return v[3:], pyVar + } return v, cVar } return name, goVar } -func (p *context) varOf(v *ssa.Global) (ret llssa.Global) { +func (p *context) varOf(b llssa.Builder, v *ssa.Global) llssa.Expr { pkgTypes := p.ensureLoaded(v.Pkg.Pkg) pkg := p.pkg name, vtype := p.varName(pkgTypes, v) - if ret = pkg.VarOf(name); ret == nil { + if vtype == pyVar { + if kind, mod := pkgKindByScope(pkgTypes.Scope()); kind == PkgPyModule { + return b.PyNewVar(pysymPrefix+mod, name).Expr + } + panic("unreachable") + } + ret := pkg.VarOf(name) + if ret == nil { ret = pkg.NewVar(name, v.Type(), llssa.Background(vtype)) } - return + return ret.Expr } func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package { diff --git a/py/math/llgo_autogen.lla b/py/math/llgo_autogen.lla index a1199546..3e07c715 100644 Binary files a/py/math/llgo_autogen.lla and b/py/math/llgo_autogen.lla differ diff --git a/ssa/decl.go b/ssa/decl.go index 181e47e4..fd8ecefc 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -282,6 +282,29 @@ func (p Function) Block(idx int) BasicBlock { // ----------------------------------------------------------------------------- +type aPyGlobal struct { + Expr +} + +type PyGlobal = *aPyGlobal + +// PyNewVar creates a Python variable. +func (b Builder) PyNewVar(modName, name string) PyGlobal { + pkg := b.Func.Pkg + modPtr := pkg.PyNewModVar(modName, false).Expr + mod := b.Load(modPtr) + return &aPyGlobal{pyVarExpr(mod, name)} +} + +func (b Builder) pyLoad(ptr Expr) Expr { + pkg := b.Func.Pkg + t := ptr.raw.Type.(*pyVarTy) + fn := pkg.pyFunc("PyObject_GetAttrString", b.Prog.tyGetAttrString()) + return b.Call(fn, t.mod, b.CStr(t.name)) +} + +// ----------------------------------------------------------------------------- + type aPyObjRef struct { Expr Obj Global @@ -290,8 +313,8 @@ type aPyObjRef struct { // PyObjRef represents a python object reference. type PyObjRef = *aPyObjRef -// NewPyFunc creates a new python function. -func (p Package) NewPyFunc(name string, sig *types.Signature, doInit bool) PyObjRef { +// PyNewFunc creates a new python function. +func (p Package) PyNewFunc(name string, sig *types.Signature, doInit bool) PyObjRef { if v, ok := p.pyobjs[name]; ok { return v } diff --git a/ssa/expr.go b/ssa/expr.go index 420ceb97..a9d1d1ef 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -54,6 +54,26 @@ func (v Expr) Do(b Builder) Expr { // ----------------------------------------------------------------------------- +type pyVarTy struct { + mod Expr + name string +} + +func (p pyVarTy) Underlying() types.Type { + panic("don't call") +} + +func (p pyVarTy) String() string { + return "pyVar" +} + +func pyVarExpr(mod Expr, name string) Expr { + tvar := &aType{raw: rawType{&pyVarTy{mod, name}}, kind: vkPyVarRef} + return Expr{Type: tvar} +} + +// ----------------------------------------------------------------------------- + type phisExprTy struct { phis []llvm.Value Type @@ -68,7 +88,8 @@ func (p phisExprTy) String() string { } func phisExpr(t Type, phis []llvm.Value) Expr { - return Expr{Type: &aType{raw: rawType{&phisExprTy{phis, t}}, kind: vkPhisExpr}} + tphi := &aType{raw: rawType{&phisExprTy{phis, t}}, kind: vkPhisExpr} + return Expr{Type: tphi} } // ----------------------------------------------------------------------------- @@ -513,6 +534,9 @@ func (b Builder) Load(ptr Expr) Expr { if debugInstr { log.Printf("Load %v\n", ptr.impl) } + if ptr.kind == vkPyVarRef { + return b.pyLoad(ptr) + } telem := b.Prog.Elem(ptr.Type) return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem} } diff --git a/ssa/package.go b/ssa/package.go index 11b68901..bcfe246e 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -146,6 +146,7 @@ type aProgram struct { callOneArg *types.Signature callFOArgs *types.Signature loadPyModS *types.Signature + getAttrStr *types.Signature paramObjPtr_ *types.Var @@ -497,6 +498,7 @@ func (p Program) paramObjPtr() *types.Var { return p.paramObjPtr_ } +// func(*char) *Object func (p Program) tyImportPyModule() *types.Signature { if p.pyImpTy == nil { charPtr := types.NewPointer(types.Typ[types.Int8]) @@ -507,6 +509,7 @@ func (p Program) tyImportPyModule() *types.Signature { return p.pyImpTy } +// func(*Object) *Object func (p Program) tyCallNoArgs() *types.Signature { if p.callNoArgs == nil { params := types.NewTuple(p.paramObjPtr()) @@ -515,6 +518,7 @@ func (p Program) tyCallNoArgs() *types.Signature { return p.callNoArgs } +// func(*Object, *Object) *Object func (p Program) tyCallOneArg() *types.Signature { if p.callOneArg == nil { paramObjPtr := p.paramObjPtr() @@ -525,6 +529,7 @@ func (p Program) tyCallOneArg() *types.Signature { return p.callOneArg } +// func(*Object, ...) *Object func (p Program) tyCallFunctionObjArgs() *types.Signature { if p.callFOArgs == nil { paramObjPtr := p.paramObjPtr() @@ -536,6 +541,7 @@ func (p Program) tyCallFunctionObjArgs() *types.Signature { } /* +// func(*Object, *Object, *Object) *Object func (p Program) tyCall() *types.Signature { if p.callArgs == nil { paramObjPtr := p.paramObjPtr() @@ -547,6 +553,7 @@ func (p Program) tyCall() *types.Signature { } */ +// func(*Object, uintptr, *Object) cint func (p Program) tyListSetItem() *types.Signature { if p.pyListSetI == nil { paramUintptr := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type) @@ -559,6 +566,7 @@ func (p Program) tyListSetItem() *types.Signature { return p.pyListSetI } +// func(uintptr) *Object func (p Program) tyNewList() *types.Signature { if p.pyNewList == nil { paramUintptr := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type) @@ -569,6 +577,7 @@ func (p Program) tyNewList() *types.Signature { return p.pyNewList } +// func(float64) *Object func (p Program) tyFloatFromDouble() *types.Signature { if p.callArgs == nil { paramObjPtr := p.paramObjPtr() @@ -580,29 +589,41 @@ func (p Program) tyFloatFromDouble() *types.Signature { return p.callArgs } +// func(*Object, ...) func (p Program) tyLoadPyModSyms() *types.Signature { if p.loadPyModS == nil { - objPtr := p.PyObjectPtr().raw.Type - paramObjPtr := types.NewParam(token.NoPos, nil, "mod", objPtr) + paramObjPtr := p.paramObjPtr() params := types.NewTuple(paramObjPtr, VArg()) p.loadPyModS = types.NewSignatureType(nil, nil, nil, params, nil, true) } return p.loadPyModS } +// func(*Objecg, *char) *Object +func (p Program) tyGetAttrString() *types.Signature { + if p.getAttrStr == nil { + charPtr := types.NewPointer(types.Typ[types.Int8]) + paramObjPtr := p.paramObjPtr() + params := types.NewTuple(paramObjPtr, types.NewParam(token.NoPos, nil, "", charPtr)) + results := types.NewTuple(paramObjPtr) + p.getAttrStr = types.NewSignatureType(nil, nil, nil, params, results, false) + } + return p.getAttrStr +} + // PyInit initializes Python for a main package. func (p Package) PyInit() bool { if fn := p.FuncOf("main"); fn != nil { b := fn.NewBuilder() - b.SetBlockEx(fn.Block(0), AtStart).CallPyInit() + b.SetBlockEx(fn.Block(0), AtStart).callPyInit() b.Dispose() return true } return false } -// NewPyModVar creates a new global variable for a Python module. -func (p Package) NewPyModVar(name string, doInit bool) Global { +// PyNewModVar creates a new global variable for a Python module. +func (p Package) PyNewModVar(name string, doInit bool) Global { if v, ok := p.pymods[name]; ok { return v } @@ -617,18 +638,18 @@ func (p Package) NewPyModVar(name string, doInit bool) Global { return g } -// ImportPyMod imports a Python module. -func (b Builder) ImportPyMod(path string) Expr { +// PyImportMod imports a Python module. +func (b Builder) PyImportMod(path string) Expr { pkg := b.Func.Pkg fnImp := pkg.pyFunc("PyImport_ImportModule", b.Prog.tyImportPyModule()) return b.Call(fnImp, b.CStr(path)) } -// LoadPyModSyms loads python objects from specified module. -func (b Builder) LoadPyModSyms(modName string, objs ...PyObjRef) Expr { +// PyLoadModSyms loads python objects from specified module. +func (b Builder) PyLoadModSyms(modName string, objs ...PyObjRef) Expr { pkg := b.Func.Pkg fnLoad := pkg.pyFunc("llgoLoadPyModSyms", b.Prog.tyLoadPyModSyms()) - modPtr := pkg.NewPyModVar(modName, false).Expr + modPtr := pkg.PyNewModVar(modName, false).Expr mod := b.Load(modPtr) args := make([]Expr, 1, len(objs)*2+2) args[0] = mod @@ -724,8 +745,8 @@ func (b Builder) PyFloat(fltVal Expr) (ret Expr) { return b.Call(fn, fltVal) } -// CallPyInit calls Py_Initialize. -func (b Builder) CallPyInit() (ret Expr) { +// callPyInit calls Py_Initialize. +func (b Builder) callPyInit() (ret Expr) { fn := b.Func.Pkg.pyFunc("Py_Initialize", NoArgsNoRet) return b.Call(fn) } diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index be584c08..13a98552 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -102,7 +102,9 @@ func TestCvtType(t *testing.T) { func TestUserdefExpr(t *testing.T) { b := &phisExprTy{} + c := &pyVarTy{} _ = b.String() + _ = c.String() test := func(a types.Type) { defer func() { if r := recover(); r == nil { @@ -112,6 +114,7 @@ func TestUserdefExpr(t *testing.T) { a.Underlying() } test(b) + test(c) } func TestAny(t *testing.T) { @@ -143,12 +146,12 @@ func TestPyFunc(t *testing.T) { prog.SetPython(py) pkg := prog.NewPackage("bar", "foo/bar") sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) - a := pkg.NewPyFunc("a", sig, false) - if pkg.NewPyFunc("a", sig, false) != a { + a := pkg.PyNewFunc("a", sig, false) + if pkg.PyNewFunc("a", sig, false) != a { t.Fatal("NewPyFunc(a) failed") } - foo := pkg.NewPyModVar("foo", false) - if pkg.NewPyModVar("foo", false) != foo { + foo := pkg.PyNewModVar("foo", false) + if pkg.PyNewModVar("foo", false) != foo { t.Fatal("NewPyModVar(foo) failed") } } diff --git a/ssa/type.go b/ssa/type.go index 18f35630..6814b07e 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -44,6 +44,7 @@ const ( vkFuncPtr vkClosure vkPyFuncRef + vkPyVarRef vkTuple vkSlice vkPhisExpr = -1