From 14cf646c47cee4a67f231ef9985db555f158d60b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 8 May 2024 13:12:22 +0800 Subject: [PATCH] cl: importPkg support linkname of method --- cl/builtin_test.go | 11 +++-- cl/compile_test.go | 4 ++ cl/import.go | 104 +++++++++++++++++++++++++++----------- x/sqlite/sqlite.ll | 121 +++++---------------------------------------- 4 files changed, 98 insertions(+), 142 deletions(-) diff --git a/cl/builtin_test.go b/cl/builtin_test.go index d41d1272..9c38f97c 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -160,16 +160,19 @@ func TestErrImport(t *testing.T) { func TestErrInitLinkname(t *testing.T) { var ctx context - ctx.initLinkname("foo", "//llgo:link abc", func(name string) (bool, bool) { - return false, false + ctx.initLinkname("//llgo:link abc", func(name string) (string, bool, bool) { + return "", false, false + }) + ctx.initLinkname("//go:linkname Printf printf", func(name string) (string, bool, bool) { + return "", false, false }) defer func() { if r := recover(); r == nil { t.Fatal("initLinkname: no error?") } }() - ctx.initLinkname("foo", "//go:linkname Printf printf", func(name string) (isVar bool, ok bool) { - return false, name == "Printf" + ctx.initLinkname("//go:linkname Printf printf", func(name string) (string, bool, bool) { + return "foo.Printf", false, name == "Printf" }) } diff --git a/cl/compile_test.go b/cl/compile_test.go index 2d8861cc..76be7e7a 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -40,6 +40,10 @@ func TestFromTestdata(t *testing.T) { cltest.FromDir(t, "", "./_testdata", false) } +func TestSqlite(t *testing.T) { + cltest.Pkg(t, "github.com/goplus/llgo/x/sqlite", "../x/sqlite/sqlite.ll") +} + func TestRuntime(t *testing.T) { cltest.Pkg(t, ssa.PkgRuntime, "../internal/runtime/llgo_autogen.ll") } diff --git a/cl/import.go b/cl/import.go index 4951e793..10e5fedd 100644 --- a/cl/import.go +++ b/cl/import.go @@ -18,6 +18,7 @@ package cl import ( "bytes" + "fmt" "go/ast" "go/constant" "go/token" @@ -30,8 +31,9 @@ import ( ) type symInfo struct { - file string - isVar bool + file string + fullName string + isVar bool } type pkgSymInfo struct { @@ -46,7 +48,7 @@ func newPkgSymInfo() *pkgSymInfo { } } -func (p *pkgSymInfo) addSym(fset *token.FileSet, pos token.Pos, name string, isVar bool) { +func (p *pkgSymInfo) addSym(fset *token.FileSet, pos token.Pos, fullName, inPkgName string, isVar bool) { f := fset.File(pos) if fp := f.Position(pos); fp.Line > 2 { file := fp.Filename @@ -56,17 +58,17 @@ func (p *pkgSymInfo) addSym(fset *token.FileSet, pos token.Pos, name string, isV p.files[file] = b } } - p.syms[name] = symInfo{file, isVar} + p.syms[inPkgName] = symInfo{file, fullName, isVar} } } -func (p *pkgSymInfo) initLinknames(ctx *context, pkgPath string) { +func (p *pkgSymInfo) initLinknames(ctx *context) { for file, b := range p.files { lines := bytes.Split(b, []byte{'\n'}) for _, line := range lines { - ctx.initLinkname(pkgPath, string(line), func(name string) (isVar bool, ok bool) { - if sym, ok := p.syms[name]; ok && file == sym.file { - return sym.isVar, true + ctx.initLinkname(string(line), func(inPkgName string) (fullName string, isVar, ok bool) { + if sym, ok := p.syms[inPkgName]; ok && file == sym.file { + return sym.fullName, sym.isVar, true } return }) @@ -116,6 +118,7 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) { } i.kind = kind fset := p.fset + pkgPath := llssa.PathOf(pkg) names := scope.Names() syms := newPkgSymInfo() for _, name := range names { @@ -124,16 +127,17 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) { switch obj := obj.(type) { case *types.Func: if pos := obj.Pos(); pos != token.NoPos { - syms.addSym(fset, pos, name, false) + fullName, inPkgName := typesFuncName(pkgPath, obj) + syms.addSym(fset, pos, fullName, inPkgName, false) } case *types.Var: if pos := obj.Pos(); pos != token.NoPos { - syms.addSym(fset, pos, name, true) + syms.addSym(fset, pos, pkgPath+"."+name, name, true) } } } } - syms.initLinknames(p, llssa.PathOf(pkg)) + syms.initLinknames(p) } func (p *context) initFiles(pkgPath string, files []*ast.File) { @@ -141,13 +145,13 @@ func (p *context) initFiles(pkgPath string, files []*ast.File) { for _, decl := range file.Decls { switch decl := decl.(type) { case *ast.FuncDecl: - if decl.Recv == nil { - p.initLinknameByDoc(decl.Doc, pkgPath, decl.Name.Name, false) - } + fullName, inPkgName := astFuncName(pkgPath, decl) + p.initLinknameByDoc(decl.Doc, fullName, inPkgName, false) case *ast.GenDecl: if decl.Tok == token.VAR && len(decl.Specs) == 1 { if names := decl.Specs[0].(*ast.ValueSpec).Names; len(names) == 1 { - p.initLinknameByDoc(decl.Doc, pkgPath, names[0].Name, true) + inPkgName := names[0].Name + p.initLinknameByDoc(decl.Doc, pkgPath+"."+inPkgName, inPkgName, true) } } } @@ -155,50 +159,92 @@ func (p *context) initFiles(pkgPath string, files []*ast.File) { } } -func (p *context) initLinknameByDoc(doc *ast.CommentGroup, pkgPath, expName string, isVar bool) { +func (p *context) initLinknameByDoc(doc *ast.CommentGroup, fullName, inPkgName string, isVar bool) { if doc != nil { if n := len(doc.List); n > 0 { line := doc.List[n-1].Text - p.initLinkname(pkgPath, line, func(name string) (bool, bool) { - return isVar, name == expName + p.initLinkname(line, func(name string) (_ string, _, ok bool) { + if name == inPkgName { + return fullName, isVar, true + } + return }) } } } -func (p *context) initLinkname(pkgPath, line string, f func(name string) (isVar bool, ok bool)) { +func (p *context) initLinkname(line string, f func(inPkgName string) (fullName string, isVar, ok bool)) { const ( linkname = "//go:linkname " llgolink = "//llgo:link " llgolink2 = "// llgo:link " ) if strings.HasPrefix(line, linkname) { - p.initLink(pkgPath, line, len(linkname), f) + p.initLink(line, len(linkname), f) } else if strings.HasPrefix(line, llgolink2) { - p.initLink(pkgPath, line, len(llgolink2), f) + p.initLink(line, len(llgolink2), f) } else if strings.HasPrefix(line, llgolink) { - p.initLink(pkgPath, line, len(llgolink), f) + p.initLink(line, len(llgolink), f) } } -func (p *context) initLink(pkgPath string, line string, prefix int, f func(name string) (isVar bool, ok bool)) { +func (p *context) initLink(line string, prefix int, f func(inPkgName string) (fullName string, isVar, ok bool)) { text := strings.TrimSpace(line[prefix:]) if idx := strings.IndexByte(text, ' '); idx > 0 { - name := text[:idx] - if isVar, ok := f(name); ok { + inPkgName := text[:idx] + if fullName, isVar, ok := f(inPkgName); ok { link := strings.TrimLeft(text[idx+1:], " ") if isVar || strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr - name := pkgPath + "." + name - p.link[name] = link + p.link[fullName] = link } else { panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf") } + } else { + fmt.Fprintln(os.Stderr, "==>", line) + fmt.Fprintf(os.Stderr, "linkname %s not found and ignored\n", inPkgName) } } } -// func: pkg.name -// method: (pkg.T).name, (*pkg.T).name +// inPkgName: +// - func: name +// - method: (T).name, (*T).name +// fullName: +// - func: pkg.name +// - method: (pkg.T).name, (*pkg.T).name +func astFuncName(pkgPath string, fn *ast.FuncDecl) (fullName, inPkgName string) { + name := fn.Name.Name + if recv := fn.Recv; recv != nil && len(recv.List) == 1 { + tPrefix := "(" + t := recv.List[0].Type + if tp, ok := t.(*ast.StarExpr); ok { + t, tPrefix = tp.X, "(*" + } + tSuffix := t.(*ast.Ident).Name + ")." + name + return tPrefix + pkgPath + "." + tSuffix, tPrefix + tSuffix + } + return pkgPath + "." + name, name +} + +func typesFuncName(pkgPath string, fn *types.Func) (fullName, inPkgName string) { + sig := fn.Type().(*types.Signature) + name := fn.Name() + if recv := sig.Recv(); recv != nil { + tPrefix := "(" + t := recv.Type() + if tp, ok := t.(*types.Pointer); ok { + t, tPrefix = tp.Elem(), "(*" + } + tSuffix := t.(*types.Named).Obj().Name() + ")." + name + return tPrefix + pkgPath + "." + tSuffix, tPrefix + tSuffix + } + return pkgPath + "." + name, name +} + +// TODO(xsw): may can use typesFuncName +// fullName: +// - func: pkg.name +// - method: (pkg.T).name, (*pkg.T).name func funcName(pkg *types.Package, fn *ssa.Function) string { sig := fn.Signature name := fn.Name() diff --git a/x/sqlite/sqlite.ll b/x/sqlite/sqlite.ll index a8a85f19..c0e3dac3 100644 --- a/x/sqlite/sqlite.ll +++ b/x/sqlite/sqlite.ll @@ -5,15 +5,10 @@ source_filename = "github.com/goplus/llgo/x/sqlite" @"github.com/goplus/llgo/x/sqlite.init$guard" = global ptr null -define ptr @"(github.com/goplus/llgo/x/sqlite.Errno).Errstr"(i32 %0) { -_llgo_0: - ret ptr null -} - define ptr @"(*github.com/goplus/llgo/x/sqlite.Errno).Errstr"(ptr %0) { _llgo_0: %1 = load i32, ptr %0, align 4 - %2 = call ptr @"(github.com/goplus/llgo/x/sqlite.Errno).Errstr"(i32 %1) + %2 = call ptr @sqlite3_errstr() ret ptr %2 } @@ -37,43 +32,13 @@ _llgo_0: ret { ptr, i32 } %mrv1 } -define i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).Close"(ptr %0) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).CloseV2"(ptr %0) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).Errcode"(ptr %0) { -_llgo_0: - ret i32 0 -} - -define ptr @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).Errmsg"(ptr %0) { -_llgo_0: - ret ptr null -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).Exec"(ptr %0, ptr %1, { ptr, ptr } %2, ptr %3, ptr %4) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).ExtendedErrcode"(ptr %0) { -_llgo_0: - ret i32 0 -} - define { ptr, i32 } @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).Prepare"(ptr %0, %"github.com/goplus/llgo/internal/runtime.String" %1, ptr %2) { _llgo_0: %3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) %4 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %1) %5 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %1) %6 = trunc i64 %5 to i32 - %7 = call i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).doPrepare"(ptr %0, ptr %4, i32 %6, ptr %3, ptr %2) + %7 = call i32 @sqlite3_prepare(ptr %0, ptr %4, i32 %6, ptr %3, ptr %2) %8 = load ptr, ptr %3, align 8 %mrv = insertvalue { ptr, i32 } poison, ptr %8, 0 %mrv1 = insertvalue { ptr, i32 } %mrv, i32 %7, 1 @@ -86,7 +51,7 @@ _llgo_0: %4 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %1) %5 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %1) %6 = trunc i64 %5 to i32 - %7 = call i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).doPrepareV2"(ptr %0, ptr %4, i32 %6, ptr %3, ptr %2) + %7 = call i32 @sqlite3_prepare_v2(ptr %0, ptr %4, i32 %6, ptr %3, ptr %2) %8 = load ptr, ptr %3, align 8 %mrv = insertvalue { ptr, i32 } poison, ptr %8, 0 %mrv1 = insertvalue { ptr, i32 } %mrv, i32 %7, 1 @@ -99,83 +64,13 @@ _llgo_0: %5 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %1) %6 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %1) %7 = trunc i64 %6 to i32 - %8 = call i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).doPrepareV3"(ptr %0, ptr %5, i32 %7, i32 %2, ptr %4, ptr %3) + %8 = call i32 @sqlite3_prepare_v3(ptr %0, ptr %5, i32 %7, i32 %2, ptr %4, ptr %3) %9 = load ptr, ptr %4, align 8 %mrv = insertvalue { ptr, i32 } poison, ptr %9, 0 %mrv1 = insertvalue { ptr, i32 } %mrv, i32 %8, 1 ret { ptr, i32 } %mrv1 } -define i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).doPrepare"(ptr %0, ptr %1, i32 %2, ptr %3, ptr %4) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).doPrepareV2"(ptr %0, ptr %1, i32 %2, ptr %3, ptr %4) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).doPrepareV3"(ptr %0, ptr %1, i32 %2, i32 %3, ptr %4, ptr %5) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Stmt).BindInt"(ptr %0, i32 %1, i32 %2) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Stmt).BindInt64"(ptr %0, i32 %1, i64 %2) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Stmt).BindText"(ptr %0, i32 %1, ptr %2, i32 %3, { ptr, ptr } %4) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Stmt).Close"(ptr %0) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Stmt).ColumnCount"(ptr %0) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Stmt).ColumnInt"(ptr %0, i32 %1) { -_llgo_0: - ret i32 0 -} - -define i64 @"(*github.com/goplus/llgo/x/sqlite.Stmt).ColumnInt64"(ptr %0, i32 %1) { -_llgo_0: - ret i64 0 -} - -define ptr @"(*github.com/goplus/llgo/x/sqlite.Stmt).ColumnName"(ptr %0, i32 %1) { -_llgo_0: - ret ptr null -} - -define ptr @"(*github.com/goplus/llgo/x/sqlite.Stmt).ColumnText"(ptr %0, i32 %1) { -_llgo_0: - ret ptr null -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Stmt).Reset"(ptr %0) { -_llgo_0: - ret i32 0 -} - -define i32 @"(*github.com/goplus/llgo/x/sqlite.Stmt).Step"(ptr %0) { -_llgo_0: - ret i32 0 -} - define void @"github.com/goplus/llgo/x/sqlite.init"() { _llgo_0: %0 = load i1, ptr @"github.com/goplus/llgo/x/sqlite.init$guard", align 1 @@ -189,6 +84,8 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } +declare ptr @sqlite3_errstr() + declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) declare i32 @sqlite3_open(ptr, ptr) @@ -198,3 +95,9 @@ declare i32 @sqlite3_open_v2(ptr, ptr, i32, ptr) declare ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String") declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String") + +declare i32 @sqlite3_prepare(ptr, i32, ptr, ptr) + +declare i32 @sqlite3_prepare_v2(ptr, i32, ptr, ptr) + +declare i32 @sqlite3_prepare_v3(ptr, i32, i32, ptr, ptr)