llgo/ssa: PyFunction; NewPyFunc

This commit is contained in:
xushiwei
2024-05-11 21:55:50 +08:00
parent 15499ddc14
commit a2d7a8c978
8 changed files with 129 additions and 68 deletions

12
cl/_testpy/callpy/in.go Normal file
View File

@@ -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())
}

0
cl/_testpy/callpy/out.ll Normal file
View File

View File

@@ -211,19 +211,19 @@ var (
argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8])) 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) pkgTypes, name, ftype := p.funcName(f, true)
if ftype != goFunc { if ftype != goFunc {
if ftype == pyFunc { if ftype == pyFunc {
// TODO(xsw): pyMod == "" // TODO(xsw): pyMod == ""
fn := pysymPrefix + p.pyMod + "." + name fnName := pysymPrefix + p.pyMod + "." + name
pkg.NewVar(fn, pkg.Prog.PyObjectPtrPtr().RawType(), llssa.InC) return nil, pkg.NewPyFunc(fnName, f.Signature), pyFunc
} }
return nil return nil, nil, ignoredFunc
} }
fn := pkg.FuncOf(name) fn := pkg.FuncOf(name)
if fn != nil && fn.HasBody() { if fn != nil && fn.HasBody() {
return fn return fn, nil, goFunc
} }
var sig = f.Signature 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. // funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
// or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, 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) _, 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 { switch name {
case "cstr": case "cstr":
ftype = llgoCstr ftype = llgoCstr
@@ -307,11 +313,14 @@ func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
default: default:
panic("unknown llgo instruction: " + name) panic("unknown llgo instruction: " + name)
} }
} else { default:
pkg := p.pkg 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 sig := fn.Signature
ret = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false) aFn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false)
} }
} }
return return
@@ -542,11 +551,13 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
ret = b.BuiltinCall(fn, args...) ret = b.BuiltinCall(fn, args...)
} }
case *ssa.Function: case *ssa.Function:
fn, ftype := p.compileFunction(cv) aFn, pyFn, ftype := p.compileFunction(cv)
switch ftype { switch ftype {
case goFunc, cFunc: case goFunc, cFunc:
args := p.compileValues(b, args, kind) 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: case llgoCstr:
ret = cstr(b, args) ret = cstr(b, args)
case llgoAdvance: 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? // v.Pkg == nil: means auto generated function?
if v.Pkg == p.goPkg || v.Pkg == nil { if v.Pkg == p.goPkg || v.Pkg == nil {
// function in this package // function in this package
if fn := p.compileFuncDecl(p.pkg, v); fn != nil { goFn, pyFn, kind = p.compileFuncDecl(p.pkg, v)
return fn, goFunc if kind != ignoredFunc {
return
} }
} }
return p.funcOf(v) return p.funcOf(v)
@@ -766,8 +778,11 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
} }
} }
case *ssa.Function: case *ssa.Function:
fn, _ := p.compileFunction(v) aFn, pyFn, _ := p.compileFunction(v)
return fn.Expr if aFn != nil {
return aFn.Expr
}
return pyFn.Expr
case *ssa.Global: case *ssa.Global:
g := p.varOf(v) g := p.varOf(v)
return g.Expr return g.Expr

View File

@@ -29,7 +29,7 @@ func testCompile(t *testing.T, src, expected string) {
} }
func TestFromTestpy(t *testing.T) { func TestFromTestpy(t *testing.T) {
cltest.FromDir(t, "", "./_testpy", false) cltest.FromDir(t, "callpy", "./_testpy", false)
} }
func TestFromTestlibc(t *testing.T) { func TestFromTestlibc(t *testing.T) {

View File

@@ -345,7 +345,7 @@ func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, strin
return nil, v[2:], cFunc return nil, v[2:], cFunc
} }
if strings.HasPrefix(v, "py.") { if strings.HasPrefix(v, "py.") {
return nil, v[3:], pyFunc return pkg, v[3:], pyFunc
} }
if strings.HasPrefix(v, "llgo.") { if strings.HasPrefix(v, "llgo.") {
return nil, v[5:], llgoInstr return nil, v[5:], llgoInstr

View File

@@ -18,6 +18,7 @@ package ssa
import ( import (
"go/types" "go/types"
"log"
"strconv" "strconv"
"github.com/goplus/llvm" "github.com/goplus/llvm"
@@ -68,6 +69,23 @@ type aGlobal struct {
// variable. // variable.
type Global = *aGlobal 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. // Init initializes the global variable with the given value.
func (g Global) Init(v Expr) { func (g Global) Init(v Expr) {
g.impl.SetInitializer(v.impl) g.impl.SetInitializer(v.impl)
@@ -140,6 +158,31 @@ type aFunction struct {
// Function represents a function or method. // Function represents a function or method.
type Function = *aFunction 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 { func newFunction(fn llvm.Value, t Type, pkg Package, prog Program, hasFreeVars bool) Function {
params, hasVArg := newParams(t, prog) params, hasVArg := newParams(t, prog)
base := 0 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]
}
// -----------------------------------------------------------------------------

View File

@@ -19,7 +19,6 @@ package ssa
import ( import (
"go/token" "go/token"
"go/types" "go/types"
"log"
"github.com/goplus/llvm" "github.com/goplus/llvm"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
@@ -260,8 +259,9 @@ func (p Program) NewPackage(name, pkgPath string) Package {
gbls := make(map[string]Global) gbls := make(map[string]Global)
fns := make(map[string]Function) fns := make(map[string]Function)
stubs := make(map[string]Function) stubs := make(map[string]Function)
pyfns := make(map[string]PyFunction)
p.needRuntime = false p.needRuntime = false
return &aPackage{mod, gbls, fns, stubs, p} return &aPackage{mod, gbls, fns, stubs, pyfns, p}
} }
// PyObjectPtrPtr returns the **py.Object type. // PyObjectPtrPtr returns the **py.Object type.
@@ -365,6 +365,7 @@ type aPackage struct {
vars map[string]Global vars map[string]Global
fns map[string]Function fns map[string]Function
stubs map[string]Function stubs map[string]Function
pyfns map[string]PyFunction
Prog Program 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 { func (p Package) rtFunc(fnName string) Expr {
fn := p.Prog.runtime().Scope().Lookup(fnName).(*types.Func) fn := p.Prog.runtime().Scope().Lookup(fnName).(*types.Func)
name := FullName(fn.Pkg(), fnName) 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) 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. // String returns a string representation of the package.
@@ -521,6 +483,13 @@ func (p Program) tyImportPyModule() *types.Signature {
return p.pyImpTy 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. // NewPyModVar creates a new global variable for a Python module.
func (p Package) NewPyModVar(name string) Global { func (p Package) NewPyModVar(name string) Global {
prog := p.Prog prog := p.Prog
@@ -531,11 +500,4 @@ func (p Package) NewPyModVar(name string) Global {
return g 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))
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -43,6 +43,7 @@ const (
vkFuncDecl vkFuncDecl
vkFuncPtr vkFuncPtr
vkClosure vkClosure
vkPyFunc
vkTuple vkTuple
vkPhisExpr = -1 vkPhisExpr = -1
) )