diff --git a/.gitignore b/.gitignore index d3ecf298..41c277a2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ _go/ _runtime/ _tinygo/ build.dir/ +.vscode/ # Test binary, built with `go test -c` *.test diff --git a/chore/gentests/gentests.go b/chore/gentests/gentests.go index b57138a1..2a8c2214 100644 --- a/chore/gentests/gentests.go +++ b/chore/gentests/gentests.go @@ -33,6 +33,7 @@ func main() { llgenDir(dir + "/cl/_testlibc") llgenDir(dir + "/cl/_testrt") + llgenDir(dir+"/cl/_testpy", "") llgenDir(dir+"/cl/_testdata", "") } diff --git a/cl/_testlibc/sqlite/out.ll b/cl/_testlibc/sqlite/out.ll index 339ca048..bdf9be1a 100644 --- a/cl/_testlibc/sqlite/out.ll +++ b/cl/_testlibc/sqlite/out.ll @@ -13,7 +13,7 @@ _llgo_0: br i1 %1, label %_llgo_1, label %_llgo_2 _llgo_1: ; preds = %_llgo_0 - %2 = call ptr @sqlite3_errstr() + %2 = call ptr @sqlite3_errstr(i32 %0) %3 = call i32 (ptr, ...) @printf(ptr @0, i32 %0, ptr %2) call void @exit(i32 1) br label %_llgo_2 @@ -45,11 +45,11 @@ _llgo_0: %3 = extractvalue { ptr, i32 } %2, 0 %4 = extractvalue { ptr, i32 } %2, 1 call void @main.check(i32 %4) - %5 = call i32 @sqlite3_close() + %5 = call i32 @sqlite3_close(ptr %3) ret void } -declare ptr @sqlite3_errstr() +declare ptr @sqlite3_errstr(i32) declare i32 @printf(ptr, ...) @@ -59,4 +59,4 @@ declare void @"github.com/goplus/llgo/internal/runtime.init"() declare { ptr, i32 } @"github.com/goplus/llgo/x/sqlite.OpenV2"(ptr, i32, ptr) -declare i32 @sqlite3_close() +declare i32 @sqlite3_close(ptr) diff --git a/cl/_testpy/callpy/out.ll b/cl/_testpy/callpy/out.ll index d3892453..1f6d541c 100644 --- a/cl/_testpy/callpy/out.ll +++ b/cl/_testpy/callpy/out.ll @@ -8,6 +8,10 @@ source_filename = "main" @__llgo_py.os.getcwd = linkonce global ptr null @0 = private unnamed_addr constant [14 x i8] c"sqrt(2) = %f\0A\00", align 1 @1 = private unnamed_addr constant [10 x i8] c"cwd = %s\0A\00", align 1 +@__llgo_py.math = external global ptr +@2 = private unnamed_addr constant [5 x i8] c"sqrt\00", align 1 +@__llgo_py.os = external global ptr +@3 = private unnamed_addr constant [7 x i8] c"getcwd\00", align 1 define void @main.init() { _llgo_0: @@ -18,6 +22,10 @@ _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"main.init$guard", align 1 call void @"github.com/goplus/llgo/py/math.init"() call void @"github.com/goplus/llgo/py/os.init"() + %1 = load ptr, ptr @__llgo_py.math, align 8 + call void (ptr, ...) @llgoLoadPyModSyms(ptr %1, ptr @2, ptr @__llgo_py.math.sqrt, ptr null) + %2 = load ptr, ptr @__llgo_py.os, align 8 + call void (ptr, ...) @llgoLoadPyModSyms(ptr %2, ptr @3, ptr @__llgo_py.os.getcwd, ptr null) br label %_llgo_2 _llgo_2: ; preds = %_llgo_1, %_llgo_0 @@ -32,12 +40,14 @@ _llgo_0: call void @"github.com/goplus/llgo/internal/runtime.init"() call void @main.init() %2 = call ptr @PyFloat_FromDouble(double 2.000000e+00) - %3 = call ptr @PyObject_CallOneArg(ptr @__llgo_py.math.sqrt, ptr %2) - %4 = call ptr @PyObject_CallNoArgs(ptr @__llgo_py.os.getcwd) - %5 = call double @PyFloat_AsDouble() - %6 = call i32 (ptr, ...) @printf(ptr @0, double %5) - %7 = call ptr @PyBytes_AsString() - %8 = call i32 (ptr, ...) @printf(ptr @1, ptr %7) + %3 = load ptr, ptr @__llgo_py.math.sqrt, align 8 + %4 = call ptr @PyObject_CallOneArg(ptr %3, ptr %2) + %5 = load ptr, ptr @__llgo_py.os.getcwd, align 8 + %6 = call ptr @PyObject_CallNoArgs(ptr %5) + %7 = call double @PyFloat_AsDouble(ptr %4) + %8 = call i32 (ptr, ...) @printf(ptr @0, double %7) + %9 = call ptr @PyBytes_AsString(ptr %6) + %10 = call i32 (ptr, ...) @printf(ptr @1, ptr %9) ret void } @@ -53,10 +63,12 @@ declare ptr @PyObject_CallOneArg(ptr, ptr) declare ptr @PyObject_CallNoArgs(ptr) -declare double @PyFloat_AsDouble() +declare double @PyFloat_AsDouble(ptr) declare i32 @printf(ptr, ...) -declare ptr @PyBytes_AsString() +declare ptr @PyBytes_AsString(ptr) + +declare void @llgoLoadPyModSyms(ptr, ...) declare void @Py_Initialize() diff --git a/cl/_testpy/math/out.ll b/cl/_testpy/math/out.ll index f5feead3..378284c6 100644 --- a/cl/_testpy/math/out.ll +++ b/cl/_testpy/math/out.ll @@ -5,6 +5,7 @@ source_filename = "math" @"math.init$guard" = global ptr null @__llgo_py.math = linkonce global ptr null @0 = private unnamed_addr constant [5 x i8] c"math\00", align 1 +@1 = private unnamed_addr constant [5 x i8] c"sqrt\00", align 1 define void @math.init() { _llgo_0: @@ -14,16 +15,20 @@ _llgo_0: _llgo_1: ; preds = %_llgo_0 store i1 true, ptr @"math.init$guard", align 1 %1 = load ptr, ptr @__llgo_py.math, align 8 - %2 = icmp ne ptr %1, null - br i1 %2, label %_llgo_2, label %_llgo_3 + call void (ptr, ...) @llgoLoadPyModSyms(ptr %1, ptr @1, ptr @__llgo_py.math.sqrt, ptr null) + %2 = load ptr, ptr @__llgo_py.math, align 8 + %3 = icmp ne ptr %2, null + br i1 %3, label %_llgo_2, label %_llgo_3 _llgo_2: ; preds = %_llgo_3, %_llgo_1, %_llgo_0 ret void _llgo_3: ; preds = %_llgo_1 - %3 = call ptr @PyImport_ImportModule(ptr @0) - store ptr %3, ptr @__llgo_py.math, align 8 + %4 = call ptr @PyImport_ImportModule(ptr @0) + store ptr %4, ptr @__llgo_py.math, align 8 br label %_llgo_2 } declare ptr @PyImport_ImportModule(ptr) + +declare void @llgoLoadPyModSyms(ptr, ...) diff --git a/cl/compile.go b/cl/compile.go index 998958ec..a786981b 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -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.PyFunction, int) { +func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function, call bool) (llssa.Function, llssa.PyObjRef, int) { pkgTypes, name, ftype := p.funcName(f, true) if ftype != goFunc { if ftype == pyFunc { @@ -250,7 +250,6 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function, call bool) } if nblk := len(f.Blocks); nblk > 0 { fn.MakeBlocks(nblk) // to set fn.HasBody() = true - isPyMod := p.pyMod != "" p.inits = append(p.inits, func() { p.fn = fn defer func() { @@ -270,9 +269,9 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function, call bool) off[i] = p.compilePhis(b, block) } for i, block := range f.Blocks { - doInit := (i == 0 && name == "main") - doPyModInit := (isPyMod && i == 1 && f.Name() == "init" && sig.Recv() == nil) - p.compileBlock(b, block, off[i], doInit, doPyModInit) + doMainInit := (i == 0 && name == "main") + doModInit := (i == 1 && f.Name() == "init" && sig.Recv() == nil) + p.compileBlock(b, block, off[i], doMainInit, doModInit) } for _, phi := range p.phis { phi() @@ -284,14 +283,14 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function, call bool) // 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) (aFn llssa.Function, pyFn llssa.PyFunction, ftype int) { +func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObjRef, ftype int) { pkgTypes, name, ftype := p.funcName(fn, false) switch ftype { case pyFunc: if kind, mod := pkgKindByScope(pkgTypes.Scope()); kind == PkgPyModule { pkg := p.pkg fnName := pysymPrefix + mod + "." + name - if pyFn = pkg.PyFuncOf(fnName); pyFn == nil { + if pyFn = pkg.PyObjOf(fnName); pyFn == nil { pyFn = pkg.NewPyFunc(fnName, fn.Signature, true) return } @@ -329,41 +328,61 @@ func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyFun return } -func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, doInit, pyModInit bool) llssa.BasicBlock { +func modOf(name string) string { + if pos := strings.LastIndexByte(name, '.'); pos > 0 { + return name[:pos] + } + return "" +} + +func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, doMainInit, doModInit bool) llssa.BasicBlock { var last int - var instrs []ssa.Instruction + var pyModInit bool + var prog = p.prog + var pkg = p.pkg + var instrs = block.Instrs[n:] var ret = p.fn.Block(block.Index) b.SetBlock(ret) - if pyModInit { - last = len(block.Instrs) - 1 - instrs = block.Instrs[n:last] - } else { - instrs = block.Instrs[n:] - if doInit { - prog := p.prog - pkg := p.pkg - fn := p.fn - argc := pkg.NewVar("__llgo_argc", types.NewPointer(types.Typ[types.Int32]), llssa.InC) - argv := pkg.NewVar("__llgo_argv", types.NewPointer(argvTy), llssa.InC) - argc.Init(prog.Null(argc.Type)) - argv.Init(prog.Null(argv.Type)) - b.Store(argc.Expr, fn.Param(0)) - b.Store(argv.Expr, fn.Param(1)) - callRuntimeInit(b, pkg) - b.Call(pkg.FuncOf("main.init").Expr) + if doModInit { + if pyModInit = p.pyMod != ""; pyModInit { + last = len(instrs) - 1 + instrs = instrs[:last] } + p.inits = append(p.inits, func() { + if objs := pkg.PyObjs(); len(objs) > 0 { + mods := make(map[string][]llssa.PyObjRef) + for name, obj := range objs { + modName := modOf(name) + mods[modName] = append(mods[modName], obj) + } + b.SetBlockEx(ret, llssa.AfterInit) + for modName, objs := range mods { + b.LoadPyModSyms(modName, objs...) + } + } + }) + } else if doMainInit { + fn := p.fn + argc := pkg.NewVar("__llgo_argc", types.NewPointer(types.Typ[types.Int32]), llssa.InC) + argv := pkg.NewVar("__llgo_argv", types.NewPointer(argvTy), llssa.InC) + argc.Init(prog.Null(argc.Type)) + argv.Init(prog.Null(argv.Type)) + b.Store(argc.Expr, fn.Param(0)) + b.Store(argv.Expr, fn.Param(1)) + callRuntimeInit(b, pkg) + b.Call(pkg.FuncOf("main.init").Expr) } for _, instr := range instrs { p.compileInstr(b, instr) } if pyModInit { - jump := block.Instrs[last].(*ssa.Jump) + jump := block.Instrs[n+last].(*ssa.Jump) jumpTo := p.jumpTo(jump) modPath := p.pyMod modName := pysymPrefix + modPath - modPtr := p.pkg.NewPyModVar(modName).Expr + modPtr := pkg.NewPyModVar(modName, true).Expr mod := b.Load(modPtr) - cond := b.BinOp(token.NEQ, mod, b.Prog.Null(mod.Type)) + cond := b.BinOp(token.NEQ, mod, prog.Null(mod.Type)) newBlk := p.fn.MakeBlock() b.If(cond, jumpTo, newBlk) b.SetBlock(newBlk) @@ -763,7 +782,7 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { } } -func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyFunction, kind int) { +func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyObjRef, kind int) { // v.Pkg == nil: means auto generated function? if v.Pkg == p.goPkg || v.Pkg == nil { // function in this package diff --git a/py/_pyg/module.c b/py/_pyg/module.c new file mode 100644 index 00000000..7ce91821 --- /dev/null +++ b/py/_pyg/module.c @@ -0,0 +1,25 @@ +#include +#include + +// example: +// llgoLoadPyModSyms(mod, "name1", &func1, "name2", &func2, NULL) + +typedef struct PyObject PyObject; + +PyObject* PyObject_GetAttrString(PyObject* mod, const char* attrName); + +void llgoLoadPyModSyms(PyObject* mod, ...) { + va_list ap; + va_start(ap, mod); + for (;;) { + const char* name = va_arg(ap, const char*); + if (name == NULL) { + break; + } + PyObject** pfunc = va_arg(ap, PyObject**); + if (*pfunc == NULL) { + *pfunc = PyObject_GetAttrString(mod, name); + } + } + va_end(ap); +} diff --git a/py/llgo.cfg b/py/llgo.cfg new file mode 100644 index 00000000..09270308 --- /dev/null +++ b/py/llgo.cfg @@ -0,0 +1,6 @@ +{ + "cl": [ + "clang -emit-llvm -S -o module.ll -c _pyg/module.c", + "rm llgo_autogen.lla; zip llgo_autogen.lla llgo_autogen.ll module.ll", + ] +} diff --git a/py/llgo_autogen.lla b/py/llgo_autogen.lla index 79b52437..ab226c5e 100644 Binary files a/py/llgo_autogen.lla and b/py/llgo_autogen.lla differ diff --git a/py/math/llgo_autogen.lla b/py/math/llgo_autogen.lla index 7c9261cd..8eaa159b 100644 Binary files a/py/math/llgo_autogen.lla and b/py/math/llgo_autogen.lla differ diff --git a/py/module.go b/py/module.go index 59e669c5..17f19b14 100644 --- a/py/module.go +++ b/py/module.go @@ -50,4 +50,7 @@ func Import(name *Object) *Object // llgo:link (*Object).ModuleGetDict C.PyModule_GetDict func (m *Object) ModuleGetDict() *Object { return nil } +// llgo:link (*Object).ModuleLoadSyms C.llgoLoadPyModSyms +func (m *Object) ModuleLoadSyms(__llgo_va_list ...any) {} + // ----------------------------------------------------------------------------- diff --git a/py/module.ll b/py/module.ll new file mode 100644 index 00000000..d11c7450 --- /dev/null +++ b/py/module.ll @@ -0,0 +1,82 @@ +; ModuleID = '_pyg/module.c' +source_filename = "_pyg/module.c" + +%struct.PyObject = type opaque + +; Function Attrs: noinline nounwind optnone ssp uwtable(sync) +define void @llgoLoadPyModSyms(%struct.PyObject* noundef %0, ...) #0 { + %2 = alloca %struct.PyObject*, align 8 + %3 = alloca i8*, align 8 + %4 = alloca i8*, align 8 + %5 = alloca i8*, align 8 + %6 = alloca %struct.PyObject**, align 8 + %7 = alloca %struct.PyObject**, align 8 + store %struct.PyObject* %0, %struct.PyObject** %2, align 8 + %8 = bitcast i8** %3 to i8* + call void @llvm.va_start(i8* %8) + br label %9 + +9: ; preds = %26, %1 + %10 = va_arg i8** %3, i8* + store i8* %10, i8** %5, align 8 + %11 = load i8*, i8** %5, align 8 + store i8* %11, i8** %4, align 8 + %12 = load i8*, i8** %4, align 8 + %13 = icmp eq i8* %12, null + br i1 %13, label %14, label %15 + +14: ; preds = %9 + br label %27 + +15: ; preds = %9 + %16 = va_arg i8** %3, %struct.PyObject** + store %struct.PyObject** %16, %struct.PyObject*** %7, align 8 + %17 = load %struct.PyObject**, %struct.PyObject*** %7, align 8 + store %struct.PyObject** %17, %struct.PyObject*** %6, align 8 + %18 = load %struct.PyObject**, %struct.PyObject*** %6, align 8 + %19 = load %struct.PyObject*, %struct.PyObject** %18, align 8 + %20 = icmp eq %struct.PyObject* %19, null + br i1 %20, label %21, label %26 + +21: ; preds = %15 + %22 = load %struct.PyObject*, %struct.PyObject** %2, align 8 + %23 = load i8*, i8** %4, align 8 + %24 = call %struct.PyObject* @PyObject_GetAttrString(%struct.PyObject* noundef %22, i8* noundef %23) + %25 = load %struct.PyObject**, %struct.PyObject*** %6, align 8 + store %struct.PyObject* %24, %struct.PyObject** %25, align 8 + br label %26 + +26: ; preds = %21, %15 + br label %9 + +27: ; preds = %14 + %28 = bitcast i8** %3 to i8* + call void @llvm.va_end(i8* %28) + ret void +} + +; Function Attrs: nocallback nofree nosync nounwind willreturn +declare void @llvm.va_start(i8*) #1 + +declare %struct.PyObject* @PyObject_GetAttrString(%struct.PyObject* noundef, i8* noundef) #2 + +; Function Attrs: nocallback nofree nosync nounwind willreturn +declare void @llvm.va_end(i8*) #1 + +attributes #0 = { noinline nounwind optnone ssp uwtable(sync) "frame-pointer"="non-leaf" "min-legal-vector-width"="0" "no-trapping-math"="true" "probe-stack"="__chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+crc,+crypto,+dotprod,+fp-armv8,+fp16fml,+fullfp16,+lse,+neon,+ras,+rcpc,+rdm,+sha2,+sha3,+sm4,+v8.5a,+zcm,+zcz" } +attributes #1 = { nocallback nofree nosync nounwind willreturn } +attributes #2 = { "frame-pointer"="non-leaf" "no-trapping-math"="true" "probe-stack"="__chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+crc,+crypto,+dotprod,+fp-armv8,+fp16fml,+fullfp16,+lse,+neon,+ras,+rcpc,+rdm,+sha2,+sha3,+sm4,+v8.5a,+zcm,+zcz" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 13, i32 3]} +!1 = !{i32 1, !"wchar_size", i32 4} +!2 = !{i32 8, !"branch-target-enforcement", i32 0} +!3 = !{i32 8, !"sign-return-address", i32 0} +!4 = !{i32 8, !"sign-return-address-all", i32 0} +!5 = !{i32 8, !"sign-return-address-with-bkey", i32 0} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 1} +!8 = !{i32 7, !"frame-pointer", i32 1} +!9 = !{!"Apple clang version 14.0.3 (clang-1403.0.22.14.1)"} diff --git a/py/os/llgo_autogen.lla b/py/os/llgo_autogen.lla index a8378f7b..d9ad409c 100644 Binary files a/py/os/llgo_autogen.lla and b/py/os/llgo_autogen.lla differ diff --git a/ssa/decl.go b/ssa/decl.go index f5d1b02c..ca857cb1 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -285,36 +285,41 @@ func (p Function) Block(idx int) BasicBlock { // ----------------------------------------------------------------------------- -type aPyFunction struct { +type aPyObjRef struct { Expr Obj Global } -// PyFunction represents a python function. -type PyFunction = *aPyFunction +// 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) PyFunction { - if v, ok := p.pyfns[name]; ok { +func (p Package) NewPyFunc(name string, sig *types.Signature, doInit bool) PyObjRef { + if v, ok := p.pyobjs[name]; ok { return v } prog := p.Prog - prog.NeedPyInit = true obj := p.NewVar(name, prog.PyObjectPtrPtr().RawType(), InC) if doInit { + prog.NeedPyInit = true obj.Init(prog.Null(obj.Type)) obj.impl.SetLinkage(llvm.LinkOnceAnyLinkage) } - ty := &aType{obj.ll, rawType{sig}, vkPyFunc} + ty := &aType{obj.ll, rawType{types.NewPointer(sig)}, vkPyFuncRef} expr := Expr{obj.impl, ty} - ret := &aPyFunction{expr, obj} - p.pyfns[name] = ret + ret := &aPyObjRef{expr, obj} + p.pyobjs[name] = ret return ret } -// PyFuncOf returns a function by name. -func (p Package) PyFuncOf(name string) PyFunction { - return p.pyfns[name] +// PyObjOf returns a python object by name. +func (p Package) PyObjOf(name string) PyObjRef { + return p.pyobjs[name] +} + +// PyObjs returns all used python objects in this project. +func (p Package) PyObjs() map[string]PyObjRef { + return p.pyobjs } // ----------------------------------------------------------------------------- diff --git a/ssa/expr.go b/ssa/expr.go index a97157b8..71921b5e 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -1238,7 +1238,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { log.Println(b.String()) } var kind = fn.kind - if kind == vkPyFunc { + if kind == vkPyFuncRef { return b.pyCall(fn, args) } var ll llvm.Type diff --git a/ssa/package.go b/ssa/package.go index be863bce..eeee0f24 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -140,6 +140,7 @@ type aProgram struct { pyImpTy *types.Signature callNoArgs *types.Signature callOneArg *types.Signature + loadPyModS *types.Signature NeedRuntime bool NeedPyInit bool @@ -257,12 +258,12 @@ 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) + pyobjs := make(map[string]PyObjRef) pymods := make(map[string]Global) p.NeedRuntime = false // Don't need reset p.needPyInit here // p.needPyInit = false - return &aPackage{mod, gbls, fns, stubs, pyfns, pymods, p} + return &aPackage{mod, gbls, fns, stubs, pyobjs, pymods, p} } // PyObjectPtrPtr returns the **py.Object type. @@ -366,7 +367,7 @@ type aPackage struct { vars map[string]Global fns map[string]Function stubs map[string]Function - pyfns map[string]PyFunction + pyobjs map[string]PyObjRef pymods map[string]Global Prog Program } @@ -506,6 +507,16 @@ func (p Program) tyCallOneArg() *types.Signature { return p.callOneArg } +func (p Program) tyLoadPyModSyms() *types.Signature { + if p.loadPyModS == nil { + objPtr := p.PyObjectPtr().raw.Type + paramObjPtr := types.NewParam(token.NoPos, nil, "mod", objPtr) + params := types.NewTuple(paramObjPtr, VArg()) + p.loadPyModS = types.NewSignatureType(nil, nil, nil, params, nil, false) + } + return p.loadPyModS +} + // PyInit initializes Python for a main package. func (p Package) PyInit() bool { if fn := p.FuncOf("main"); fn != nil { @@ -518,15 +529,17 @@ func (p Package) PyInit() bool { } // NewPyModVar creates a new global variable for a Python module. -func (p Package) NewPyModVar(name string) Global { +func (p Package) NewPyModVar(name string, doInit bool) Global { if v, ok := p.pymods[name]; ok { return v } prog := p.Prog objPtr := prog.PyObjectPtrPtr().raw.Type g := p.NewVar(name, objPtr, InC) - g.Init(prog.Null(g.Type)) - g.impl.SetLinkage(llvm.LinkOnceAnyLinkage) + if doInit { + g.Init(prog.Null(g.Type)) + g.impl.SetLinkage(llvm.LinkOnceAnyLinkage) + } p.pymods[name] = g return g } @@ -538,9 +551,30 @@ func (b Builder) ImportPyMod(path string) Expr { return b.Call(fnImp, b.CStr(path)) } +// LoadPyModSyms loads python objects from specified module. +func (b Builder) LoadPyModSyms(modName string, objs ...PyObjRef) Expr { + pkg := b.Func.Pkg + fnLoad := pkg.pyFunc("llgoLoadPyModSyms", b.Prog.tyLoadPyModSyms()) + modPtr := pkg.NewPyModVar(modName, false).Expr + mod := b.Load(modPtr) + args := make([]Expr, 1, len(objs)*2+2) + args[0] = mod + nbase := len(modName) + 1 + for _, o := range objs { + fullName := o.impl.Name() + name := fullName[nbase:] + args = append(args, b.CStr(name)) + args = append(args, o.Expr) + } + prog := b.Prog + args = append(args, prog.Null(prog.CStr())) + return b.Call(fnLoad, args...) +} + func (b Builder) pyCall(fn Expr, args []Expr) (ret Expr) { prog := b.Prog pkg := b.Func.Pkg + fn = b.Load(fn) sig := fn.raw.Type.(*types.Signature) params := sig.Params() n := params.Len() diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 1afddd0d..b2bfe311 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -147,8 +147,8 @@ func TestPyFunc(t *testing.T) { if pkg.NewPyFunc("a", sig, false) != a { t.Fatal("NewPyFunc(a) failed") } - foo := pkg.NewPyModVar("foo") - if pkg.NewPyModVar("foo") != foo { + foo := pkg.NewPyModVar("foo", false) + if pkg.NewPyModVar("foo", false) != foo { t.Fatal("NewPyModVar(foo) failed") } } diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index 817d6524..579e744b 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -21,6 +21,7 @@ import ( "fmt" "go/types" "log" + "strings" "github.com/goplus/llvm" ) @@ -76,6 +77,7 @@ type InsertPoint int const ( AtEnd InsertPoint = iota AtStart + AfterInit ) // SetBlockEx sets blk as current basic block and pos as its insert point. @@ -88,12 +90,35 @@ func (b Builder) SetBlockEx(blk BasicBlock, pos InsertPoint) Builder { b.impl.SetInsertPointAtEnd(blk.impl) case AtStart: b.impl.SetInsertPointBefore(blk.impl.FirstInstruction()) + case AfterInit: + b.impl.SetInsertPointBefore(instrAfterInit(blk.impl)) default: panic("SetBlockEx: invalid pos") } return b } +func instrAfterInit(blk llvm.BasicBlock) llvm.Value { + instr := blk.FirstInstruction() + for { + instr = llvm.NextInstruction(instr) + if notInit(instr) { + return instr + } + } +} + +func notInit(instr llvm.Value) bool { + switch op := instr.InstructionOpcode(); op { + case llvm.Call: + if n := instr.OperandsCount(); n == 1 { + fn := instr.Operand(0) + return !strings.HasSuffix(fn.Name(), ".init") + } + } + return true +} + // Panic emits a panic instruction. func (b Builder) Panic(v Expr) { if debugInstr { diff --git a/ssa/type.go b/ssa/type.go index 936c9b21..735a3d90 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -43,7 +43,7 @@ const ( vkFuncDecl vkFuncPtr vkClosure - vkPyFunc + vkPyFuncRef vkTuple vkPhisExpr = -1 ) diff --git a/ssa/type_cvt.go b/ssa/type_cvt.go index 8346577e..e8bd1c3d 100644 --- a/ssa/type_cvt.go +++ b/ssa/type_cvt.go @@ -57,8 +57,11 @@ func (p Program) Type(typ types.Type, bg Background) Type { // FuncDecl converts a Go/C function declaration into raw type. func (p Program) FuncDecl(sig *types.Signature, bg Background) Type { + recv := sig.Recv() if bg == InGo { - sig = p.gocvt.cvtFunc(sig, sig.Recv()) + sig = p.gocvt.cvtFunc(sig, recv) + } else if recv != nil { // even in C, we need to add ctx for method + sig = FuncAddCtx(recv, sig) } return &aType{p.toLLVMFunc(sig), rawType{sig}, vkFuncDecl} } diff --git a/x/sqlite/llgo_autogen.lla b/x/sqlite/llgo_autogen.lla index cbd7267e..6aa88aa4 100644 Binary files a/x/sqlite/llgo_autogen.lla and b/x/sqlite/llgo_autogen.lla differ diff --git a/x/sqlite/sqlite.ll b/x/sqlite/sqlite.ll index 2acb9d94..d4bdb25a 100644 --- a/x/sqlite/sqlite.ll +++ b/x/sqlite/sqlite.ll @@ -8,7 +8,7 @@ source_filename = "github.com/goplus/llgo/x/sqlite" define ptr @"(*github.com/goplus/llgo/x/sqlite.Errno).Errstr"(ptr %0) { _llgo_0: %1 = load i32, ptr %0, align 4 - %2 = call ptr @sqlite3_errstr() + %2 = call ptr @sqlite3_errstr(i32 %1) ret ptr %2 } @@ -84,7 +84,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } -declare ptr @sqlite3_errstr() +declare ptr @sqlite3_errstr(i32) declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) @@ -92,8 +92,8 @@ declare i32 @sqlite3_open(ptr, ptr) declare i32 @sqlite3_open_v2(ptr, ptr, i32, ptr) -declare i32 @sqlite3_prepare(ptr, i32, ptr, ptr) +declare i32 @sqlite3_prepare(ptr, ptr, i32, ptr, ptr) -declare i32 @sqlite3_prepare_v2(ptr, i32, ptr, ptr) +declare i32 @sqlite3_prepare_v2(ptr, ptr, i32, ptr, ptr) -declare i32 @sqlite3_prepare_v3(ptr, i32, i32, ptr, ptr) +declare i32 @sqlite3_prepare_v3(ptr, ptr, i32, i32, ptr, ptr)