Merge pull request #133 from xushiwei/sqlite

demo: sqlite
This commit is contained in:
xushiwei
2024-05-08 23:24:12 +08:00
committed by GitHub
17 changed files with 642 additions and 140 deletions

20
cl/_testlibc/sqlite/in.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/x/sqlite"
)
func main() {
db, err := sqlite.OpenV2(c.Str(":memory:"), sqlite.OpenReadWrite|sqlite.OpenMemory, nil)
check(err)
db.Close()
}
func check(err sqlite.Errno) {
if err != sqlite.OK {
c.Printf(c.Str("==> Error: (%d) %s\n"), err, err.Errstr())
c.Exit(1)
}
}

View File

@@ -0,0 +1,62 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [20 x i8] c"==> Error: (%d) %s\0A\00", align 1
@__llgo_argc = global ptr null
@__llgo_argv = global ptr null
@1 = private unnamed_addr constant [9 x i8] c":memory:\00", align 1
define void @main.check(i32 %0) {
_llgo_0:
%1 = icmp ne i32 %0, 0
br i1 %1, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
%2 = call ptr @sqlite3_errstr()
%3 = call i32 (ptr, ...) @printf(ptr @0, i32 %0, ptr %2)
call void @exit(i32 1)
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
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
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main(i32 %0, ptr %1) {
_llgo_0:
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 = call { ptr, i32 } @"github.com/goplus/llgo/x/sqlite.OpenV2"(ptr @1, i32 130, ptr null)
%3 = extractvalue { ptr, i32 } %2, 0
%4 = extractvalue { ptr, i32 } %2, 1
call void @main.check(i32 %4)
%5 = call i32 @sqlite3_close()
ret void
}
declare ptr @sqlite3_errstr()
declare i32 @printf(ptr, ...)
declare void @exit(i32)
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()

View File

@@ -17,6 +17,7 @@
package cl
import (
"go/ast"
"go/constant"
"go/types"
"testing"
@@ -25,6 +26,27 @@ import (
"golang.org/x/tools/go/ssa"
)
func TestRecvTypeName(t *testing.T) {
if ret := recvTypeName(&ast.IndexExpr{
X: &ast.Ident{Name: "Pointer"},
Index: &ast.Ident{Name: "T"},
}); ret != "Pointer" {
t.Fatal("recvTypeName IndexExpr:", ret)
}
if ret := recvTypeName(&ast.IndexListExpr{
X: &ast.Ident{Name: "Pointer"},
Indices: []ast.Expr{&ast.Ident{Name: "T"}},
}); ret != "Pointer" {
t.Fatal("recvTypeName IndexListExpr:", ret)
}
defer func() {
if r := recover(); r == nil {
t.Fatal("recvTypeName: no error?")
}
}()
recvTypeName(&ast.BadExpr{})
}
/*
func TestErrCompileValue(t *testing.T) {
defer func() {
@@ -160,16 +182,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"
})
}

View File

@@ -123,7 +123,8 @@ const (
PkgLLGo
PkgNoInit // noinit: a package that don't need to be initialized
PkgDeclOnly // decl: a package that only have declarations
PkgLinkOnly // link: a package that don't need to be compiled but need to be linked
PkgLinkIR // link llvm ir (.ll)
// PkgLinkBitCode // link bitcode (.bc)
)
type pkgInfo struct {

View File

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

View File

@@ -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
})
@@ -89,7 +91,9 @@ func PkgKindOf(pkg *types.Package) int {
func pkgKind(v string) int {
switch v {
case "link":
return PkgLinkOnly
return PkgLinkIR
// case "link:bc":
// return PkgLinkBitCode
case "decl":
return PkgDeclOnly
case "noinit":
@@ -116,24 +120,34 @@ 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 {
if token.IsExported(name) {
obj := scope.Lookup(name)
switch obj := obj.(type) {
case *types.Func:
if pos := obj.Pos(); pos != token.NoPos {
syms.addSym(fset, pos, name, false)
}
case *types.Var:
if pos := obj.Pos(); pos != token.NoPos {
syms.addSym(fset, pos, name, true)
obj := scope.Lookup(name)
switch obj := obj.(type) {
case *types.Func:
if pos := obj.Pos(); pos != token.NoPos {
fullName, inPkgName := typesFuncName(pkgPath, obj)
syms.addSym(fset, pos, fullName, inPkgName, false)
}
case *types.TypeName:
if !obj.IsAlias() {
if t, ok := obj.Type().(*types.Named); ok {
for i, n := 0, t.NumMethods(); i < n; i++ {
fn := t.Method(i)
fullName, inPkgName := typesFuncName(pkgPath, fn)
syms.addSym(fset, fn.Pos(), fullName, inPkgName, false)
}
}
}
case *types.Var:
if pos := obj.Pos(); pos != token.NoPos {
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 +155,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 +169,107 @@ 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) {
return fullName, isVar, name == inPkgName
})
}
}
}
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, "llgo: linkname %s not found and ignored\n", inPkgName)
}
}
}
// func: pkg.name
// method: (pkg.T).name, (*pkg.T).name
func recvTypeName(t ast.Expr) string {
switch t := t.(type) {
case *ast.Ident:
return t.Name
case *ast.IndexExpr:
return trecvTypeName(t.X, t.Index)
case *ast.IndexListExpr:
return trecvTypeName(t.X, t.Indices...)
}
panic("unreachable")
}
// TODO(xsw): support generic type
func trecvTypeName(t ast.Expr, indices ...ast.Expr) string {
_ = indices
return t.(*ast.Ident).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 := recvTypeName(t) + ")." + 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()