From f0e3e556cfe88cea2bba799b053f0c48437d5b36 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 8 May 2024 10:43:10 +0800 Subject: [PATCH 1/9] demo: sqlite --- _demo/llama2-c/run.go | 16 ----- _demo/sqlite/sqlite.go | 59 ++++++++++++++++ c/c.go | 5 ++ x/sqlite/sqlite.go | 152 ++++++++++++++++++++++++++--------------- 4 files changed, 161 insertions(+), 71 deletions(-) create mode 100644 _demo/sqlite/sqlite.go diff --git a/_demo/llama2-c/run.go b/_demo/llama2-c/run.go index fe131aaf..af6acb90 100644 --- a/_demo/llama2-c/run.go +++ b/_demo/llama2-c/run.go @@ -1,19 +1,3 @@ -/* - * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package main import ( diff --git a/_demo/sqlite/sqlite.go b/_demo/sqlite/sqlite.go new file mode 100644 index 00000000..bf353160 --- /dev/null +++ b/_demo/sqlite/sqlite.go @@ -0,0 +1,59 @@ +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) + + err = db.Exec(c.Str("CREATE TABLE foo (id INT, name TEXT)"), nil, nil, nil) + check(err) + + stmt, err := db.PrepareV3("INSERT INTO foo VALUES (?, ?)", 0, nil) + check(err) + + stmt.BindInt(1, 100) + stmt.BindText(2, c.Str("Hello World"), sqlite.Static, nil) + + err = stmt.Step() + checkDone(err) + + stmt.Reset() + stmt.BindInt(1, 200) + stmt.BindText(2, c.Str("This is llgo"), sqlite.Static, nil) + + err = stmt.Step() + checkDone(err) + + stmt.Close() + + stmt, err = db.PrepareV3("SELECT * FROM foo", 0, nil) + check(err) + + for { + if err = stmt.Step(); err != sqlite.HasRow { + break + } + c.Printf(c.Str("==> id=%d, name=%s\n"), stmt.ColumnInt(0), stmt.ColumnText(1)) + } + checkDone(err) + + stmt.Close() + 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) + } +} + +func checkDone(err sqlite.Errno) { + if err != sqlite.Done { + check(err) + } +} diff --git a/c/c.go b/c/c.go index b8064409..59e09f47 100644 --- a/c/c.go +++ b/c/c.go @@ -71,6 +71,11 @@ func GoStringData(string) *Char // ----------------------------------------------------------------------------- +//go:linkname Exit C.exit +func Exit(Int) + +// ----------------------------------------------------------------------------- + //go:linkname Rand C.rand func Rand() Int diff --git a/x/sqlite/sqlite.go b/x/sqlite/sqlite.go index ca83c105..4e16c840 100644 --- a/x/sqlite/sqlite.go +++ b/x/sqlite/sqlite.go @@ -32,46 +32,6 @@ const ( LLGoPackage = "noinit,link" ) -// ----------------------------------------------------------------------------- - -type Errno Int - -const ( - OK Errno = 0 // Successful result - Error Errno = 1 // Generic error - ErrInternal Errno = 2 // Internal logic error in SQLite - ErrPerm Errno = 3 // Access permission denied - ErrAbort Errno = 4 // Callback routine requested an abort - ErrBusy Errno = 5 // The database file is locked - ErrLocked Errno = 6 // A table in the database is locked - ErrNomem Errno = 7 // A malloc() failed - ErrReadOnly Errno = 8 // Attempt to write a readonly database - ErrInterrupt Errno = 9 // Operation terminated by sqlite3_interrupt() - ErrIo Errno = 10 // Some kind of disk I/O error occurred - ErrCorrupt Errno = 11 // The database disk image is malformed - ErrNotfound Errno = 12 // Unknown opcode in sqlite3_file_control() - ErrFull Errno = 13 // Insertion failed because database is full - ErrCantopen Errno = 14 // Unable to open the database file - ErrProtocol Errno = 15 // Database lock protocol error - _ErrEmpty Errno = 16 // Internal use only - ErrSchema Errno = 17 // The database schema changed - ErrToobig Errno = 18 // String or BLOB exceeds size limit - ErrConstraint Errno = 19 // Abort due to constraint violation - ErrMismatch Errno = 20 // Data type mismatch - ErrMisuse Errno = 21 // Library used incorrectly - ErrNolfs Errno = 22 // Uses OS features not supported on host - ErrAuth Errno = 23 // Authorization denied - _ErrFormat Errno = 24 // Not used - ErrRange Errno = 25 // 2nd parameter to sqlite3_bind out of range - ErrNotadb Errno = 26 // File opened that is not a database file - ErrNotice Errno = 27 // Notifications from sqlite3_log() - ErrWarning Errno = 28 // Warnings from sqlite3_log() - ErrRow Errno = 100 // sqlite3_step() has another row ready - ErrDone Errno = 101 // sqlite3_step() has finished executing -) - -// ----------------------------------------------------------------------------- - // llgo:type C type Sqlite3 struct { } @@ -82,6 +42,58 @@ type Stmt struct { // ----------------------------------------------------------------------------- +type Errno Int + +const ( + OK Errno = 0 // Successful result + + Error Errno = 1 // Generic error + ErrInternal Errno = 2 // Internal logic error in SQLite + ErrPerm Errno = 3 // Access permission denied + ErrAbort Errno = 4 // Callback routine requested an abort + ErrBusy Errno = 5 // The database file is locked + ErrLocked Errno = 6 // A table in the database is locked + ErrNomem Errno = 7 // A malloc() failed + ErrReadOnly Errno = 8 // Attempt to write a readonly database + ErrInterrupt Errno = 9 // Operation terminated by sqlite3_interrupt() + ErrIo Errno = 10 // Some kind of disk I/O error occurred + ErrCorrupt Errno = 11 // The database disk image is malformed + ErrNotfound Errno = 12 // Unknown opcode in sqlite3_file_control() + ErrFull Errno = 13 // Insertion failed because database is full + ErrCantopen Errno = 14 // Unable to open the database file + ErrProtocol Errno = 15 // Database lock protocol error + _ErrEmpty Errno = 16 // Internal use only + ErrSchema Errno = 17 // The database schema changed + ErrToobig Errno = 18 // String or BLOB exceeds size limit + ErrConstraint Errno = 19 // Abort due to constraint violation + ErrMismatch Errno = 20 // Data type mismatch + ErrMisuse Errno = 21 // Library used incorrectly + ErrNolfs Errno = 22 // Uses OS features not supported on host + ErrAuth Errno = 23 // Authorization denied + _ErrFormat Errno = 24 // Not used + ErrRange Errno = 25 // 2nd parameter to sqlite3_bind out of range + ErrNotadb Errno = 26 // File opened that is not a database file + ErrNotice Errno = 27 // Notifications from sqlite3_log() + ErrWarning Errno = 28 // Warnings from sqlite3_log() + + HasRow Errno = 100 // sqlite3_step() has another row ready + Done Errno = 101 // sqlite3_step() has finished executing +) + +// llgo:link (Errno).Errstr C.sqlite3_errstr +func (err Errno) Errstr() *Char { return nil } + +// llgo:link (*Sqlite3).Errmsg C.sqlite3_errmsg +func (db *Sqlite3) Errmsg() *Char { return nil } + +// llgo:link (*Sqlite3).Errcode C.sqlite3_errcode +func (db *Sqlite3) Errcode() Errno { return 0 } + +// llgo:link (*Sqlite3).ExtendedErrcode C.sqlite3_extended_errcode +func (db *Sqlite3) ExtendedErrcode() Errno { return 0 } + +// ----------------------------------------------------------------------------- + //go:linkname doOpen C.sqlite3_open func doOpen(filename *Char, ppDb **Sqlite3) Errno @@ -134,12 +146,12 @@ func OpenV2(filename *Char, flags OpenFlags, zVfs *Char) (db *Sqlite3, err Errno // Closing A Database Connection // // llgo:link (*Sqlite3).Close C.sqlite3_close -func (*Sqlite3) Close() Errno { return 0 } +func (db *Sqlite3) Close() Errno { return 0 } // Closing A Database Connection // // llgo:link (*Sqlite3).CloseV2 C.sqlite3_close_v2 -func (*Sqlite3) CloseV2() Errno { return 0 } +func (db *Sqlite3) CloseV2() Errno { return 0 } // ----------------------------------------------------------------------------- @@ -168,22 +180,27 @@ const ( ) // Compiling An SQL Statement -// tail: Pointer to unused portion of zSql -func (db *Sqlite3) Prepare(sql string) (stmt *Stmt, tail *Char, err Errno) { - err = db.doPrepare(c.GoStringData(sql), c.Int(len(sql)), &stmt, &tail) +// tail: Pointer to unused portion of sql +func (db *Sqlite3) Prepare(sql string, tail **Char) (stmt *Stmt, err Errno) { + err = db.doPrepare(c.GoStringData(sql), c.Int(len(sql)), &stmt, tail) return } -func (db *Sqlite3) PrepareV2(sql string) (stmt *Stmt, tail *Char, err Errno) { - err = db.doPrepareV2(c.GoStringData(sql), c.Int(len(sql)), &stmt, &tail) +func (db *Sqlite3) PrepareV2(sql string, tail **Char) (stmt *Stmt, err Errno) { + err = db.doPrepareV2(c.GoStringData(sql), c.Int(len(sql)), &stmt, tail) return } -func (db *Sqlite3) PrepareV3(sql string, flags PrepareFlags) (stmt *Stmt, tail *Char, err Errno) { - err = db.doPrepareV3(c.GoStringData(sql), c.Int(len(sql)), flags, &stmt, &tail) +func (db *Sqlite3) PrepareV3(sql string, flags PrepareFlags, tail **Char) (stmt *Stmt, err Errno) { + err = db.doPrepareV3(c.GoStringData(sql), c.Int(len(sql)), flags, &stmt, tail) return } +// Destroy A Prepared Statement Object +// +// llgo:link (*Stmt).Close C.sqlite3_finalize +func (stmt *Stmt) Close() Errno { return 0 } + // ----------------------------------------------------------------------------- // llgo:link (*Stmt).BindInt C.sqlite3_bind_int @@ -192,15 +209,23 @@ func (*Stmt) BindInt(idx Int, val Int) Errno { return 0 } // llgo:link (*Stmt).BindInt64 C.sqlite3_bind_int64 func (*Stmt) BindInt64(idx Int, val int64) Errno { return 0 } -// llgo:link (*Stmt).doBindText C.sqlite3_bind_text -func (*Stmt) doBindText(Int, *Char, Int, func(Pointer)) Errno { return 0 } +const ( + Static Int = 0 // val is a static string + Transient Int = -1 // val is a transient (temporary) string +) -func (stmt *Stmt) BindText(idx Int, val string, xDel func(Pointer)) Errno { - return stmt.doBindText(idx, c.GoStringData(val), c.Int(len(val)), xDel) -} +// llgo:link (*Stmt).BindText C.sqlite3_bind_text +func (*Stmt) BindText(idx Int, val *Char, lenOrKind Int, destructor func(Pointer)) Errno { return 0 } // ----------------------------------------------------------------------------- +// Reset A Prepared Statement Object +// +// llgo:link (*Stmt).Reset C.sqlite3_reset +func (stmt *Stmt) Reset() Errno { + return 0 +} + // Evaluate An SQL Statement // // llgo:link (*Stmt).Step C.sqlite3_step @@ -208,12 +233,29 @@ func (*Stmt) Step() Errno { return 0 } // ----------------------------------------------------------------------------- +// llgo:link (*Stmt).ColumnCount C.sqlite3_column_count +func (stmt *Stmt) ColumnCount() Int { return 0 } + +// llgo:link (*Stmt).ColumnName C.sqlite3_column_name +func (stmt *Stmt) ColumnName(idx Int) *Char { return nil } + +// llgo:link (*Stmt).ColumnInt C.sqlite3_column_int +func (stmt *Stmt) ColumnInt(idx Int) Int { return 0 } + +// llgo:link (*Stmt).ColumnInt64 C.sqlite3_column_int64 +func (stmt *Stmt) ColumnInt64(idx Int) int64 { return 0 } + +// llgo:link (*Stmt).ColumnText C.sqlite3_column_text +func (stmt *Stmt) ColumnText(idx Int) *Char { return nil } + +// ----------------------------------------------------------------------------- + // One-Step Query Execution Interface // // llgo:link (*Sqlite3).Exec C.sqlite3_exec func (*Sqlite3) Exec( sql *Char, callback func(arg Pointer, resultCols Int, colVals, colNames **Char) Int, - arg Pointer, errmsg *Char) Errno { + arg Pointer, errmsg **Char) Errno { return 0 } From 1687faa438916143f34cc766f0ac91694eb17f47 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 8 May 2024 11:14:14 +0800 Subject: [PATCH 2/9] alias c.GoStringData = runtime.StringData --- c/c.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/c.go b/c/c.go index 59e09f47..49589a31 100644 --- a/c/c.go +++ b/c/c.go @@ -66,7 +66,7 @@ func Memset(s Pointer, c Int, n uintptr) Pointer // ----------------------------------------------------------------------------- -//go:linkname GoStringData llgo.stringData +//go:linkname GoStringData github.com/goplus/llgo/internal/runtime.StringData func GoStringData(string) *Char // ----------------------------------------------------------------------------- From d49197cbe999d44a55878b86a6002a48cfb6829d Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 8 May 2024 11:49:19 +0800 Subject: [PATCH 3/9] llgen: support sqlite.ll --- internal/llgen/llgenf.go | 13 ++- x/sqlite/llgo.cfg | 4 +- x/sqlite/sqlite.go | 2 +- x/sqlite/sqlite.ll | 200 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 x/sqlite/sqlite.ll diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go index 7326e468..11a0645d 100644 --- a/internal/llgen/llgenf.go +++ b/internal/llgen/llgenf.go @@ -75,9 +75,13 @@ func DoFile(fileOrPkg, outFile string) { func SmartDoFile(inFile string, pkgPath ...string) { dir, _ := filepath.Split(inFile) + absDir, _ := filepath.Abs(dir) + absDir = filepath.ToSlash(absDir) fname := "llgo_autogen.ll" - if inCompilerDir(dir) { + if inCompilerDir(absDir) { fname = "out.ll" + } else if inSqlite(absDir) { + fname = "sqlite.ll" } outFile := dir + fname @@ -89,6 +93,9 @@ func SmartDoFile(inFile string, pkgPath ...string) { } func inCompilerDir(dir string) bool { - dir, _ = filepath.Abs(dir) - return strings.Contains(filepath.ToSlash(dir), "/llgo/cl/") + return strings.Contains(dir, "/llgo/cl/") +} + +func inSqlite(dir string) bool { + return strings.HasSuffix(dir, "/llgo/x/sqlite") } diff --git a/x/sqlite/llgo.cfg b/x/sqlite/llgo.cfg index 718b191d..511ac2b5 100644 --- a/x/sqlite/llgo.cfg +++ b/x/sqlite/llgo.cfg @@ -4,7 +4,7 @@ "cd build.dir", "../sqlite/configure", "make", - "clang -emit-llvm -S -o ../llgo_autogen.ll -c sqlite3.c", - "zip ../llgo_autogen.lla ../llgo_autogen.ll" + "clang -emit-llvm -S -o sqlite3.ll -c sqlite3.c", + "$LLGO_LLVM_ROOT/bin/llvm-link -o ../llgo_autogen.bc ../sqlite.ll sqlite3.ll", ] } diff --git a/x/sqlite/sqlite.go b/x/sqlite/sqlite.go index 4e16c840..71fec9c0 100644 --- a/x/sqlite/sqlite.go +++ b/x/sqlite/sqlite.go @@ -29,7 +29,7 @@ type ( ) const ( - LLGoPackage = "noinit,link" + LLGoPackage = "link" ) // llgo:type C diff --git a/x/sqlite/sqlite.ll b/x/sqlite/sqlite.ll new file mode 100644 index 00000000..a8a85f19 --- /dev/null +++ b/x/sqlite/sqlite.ll @@ -0,0 +1,200 @@ +; ModuleID = 'github.com/goplus/llgo/x/sqlite' +source_filename = "github.com/goplus/llgo/x/sqlite" + +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } + +@"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) + ret ptr %2 +} + +define { ptr, i32 } @"github.com/goplus/llgo/x/sqlite.Open"(ptr %0) { +_llgo_0: + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) + %2 = call i32 @sqlite3_open(ptr %0, ptr %1) + %3 = load ptr, ptr %1, align 8 + %mrv = insertvalue { ptr, i32 } poison, ptr %3, 0 + %mrv1 = insertvalue { ptr, i32 } %mrv, i32 %2, 1 + ret { ptr, i32 } %mrv1 +} + +define { ptr, i32 } @"github.com/goplus/llgo/x/sqlite.OpenV2"(ptr %0, i32 %1, ptr %2) { +_llgo_0: + %3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) + %4 = call i32 @sqlite3_open_v2(ptr %0, ptr %3, i32 %1, ptr %2) + %5 = load ptr, ptr %3, align 8 + %mrv = insertvalue { ptr, i32 } poison, ptr %5, 0 + %mrv1 = insertvalue { ptr, i32 } %mrv, i32 %4, 1 + 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) + %8 = load ptr, ptr %3, align 8 + %mrv = insertvalue { ptr, i32 } poison, ptr %8, 0 + %mrv1 = insertvalue { ptr, i32 } %mrv, i32 %7, 1 + ret { ptr, i32 } %mrv1 +} + +define { ptr, i32 } @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).PrepareV2"(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).doPrepareV2"(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 + ret { ptr, i32 } %mrv1 +} + +define { ptr, i32 } @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).PrepareV3"(ptr %0, %"github.com/goplus/llgo/internal/runtime.String" %1, i32 %2, ptr %3) { +_llgo_0: + %4 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) + %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) + %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 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"github.com/goplus/llgo/x/sqlite.init$guard", align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) + +declare i32 @sqlite3_open(ptr, ptr) + +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") From 14cf646c47cee4a67f231ef9985db555f158d60b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 8 May 2024 13:12:22 +0800 Subject: [PATCH 4/9] 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) From 6cc58c194fb77dcac861b659d7057d45c846195f Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 8 May 2024 14:19:33 +0800 Subject: [PATCH 5/9] recvTypeName --- cl/import.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/cl/import.go b/cl/import.go index 10e5fedd..e96b9f21 100644 --- a/cl/import.go +++ b/cl/import.go @@ -206,6 +206,24 @@ func (p *context) initLink(line string, prefix int, f func(inPkgName string) (fu } } +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 @@ -220,7 +238,7 @@ func astFuncName(pkgPath string, fn *ast.FuncDecl) (fullName, inPkgName string) if tp, ok := t.(*ast.StarExpr); ok { t, tPrefix = tp.X, "(*" } - tSuffix := t.(*ast.Ident).Name + ")." + name + tSuffix := recvTypeName(t) + ")." + name return tPrefix + pkgPath + "." + tSuffix, tPrefix + tSuffix } return pkgPath + "." + name, name From cd8e1f2080ace27fa9e3baac925c15f5894c26ea Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 8 May 2024 14:48:34 +0800 Subject: [PATCH 6/9] importPkg: support method linkname --- cl/import.go | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/cl/import.go b/cl/import.go index e96b9f21..b264c542 100644 --- a/cl/import.go +++ b/cl/import.go @@ -122,19 +122,27 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) { 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 { - 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, pkgPath+"."+name, 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) @@ -201,7 +209,7 @@ func (p *context) initLink(line string, prefix int, f func(inPkgName string) (fu } } else { fmt.Fprintln(os.Stderr, "==>", line) - fmt.Fprintf(os.Stderr, "linkname %s not found and ignored\n", inPkgName) + fmt.Fprintf(os.Stderr, "llgo: linkname %s not found and ignored\n", inPkgName) } } } From b0b38c02b2ae371c649d49a13d87b6a788ffa24e Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 8 May 2024 15:01:50 +0800 Subject: [PATCH 7/9] TestRecvTypeName --- cl/_testlibc/sqlite/in.go | 20 ++++++++++++ cl/_testlibc/sqlite/out.ll | 62 ++++++++++++++++++++++++++++++++++++++ cl/builtin_test.go | 22 ++++++++++++++ cl/import.go | 5 +-- 4 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 cl/_testlibc/sqlite/in.go create mode 100644 cl/_testlibc/sqlite/out.ll diff --git a/cl/_testlibc/sqlite/in.go b/cl/_testlibc/sqlite/in.go new file mode 100644 index 00000000..b9db5a37 --- /dev/null +++ b/cl/_testlibc/sqlite/in.go @@ -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) + } +} diff --git a/cl/_testlibc/sqlite/out.ll b/cl/_testlibc/sqlite/out.ll new file mode 100644 index 00000000..339ca048 --- /dev/null +++ b/cl/_testlibc/sqlite/out.ll @@ -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() diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 9c38f97c..8d26552f 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -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() { diff --git a/cl/import.go b/cl/import.go index b264c542..299e553c 100644 --- a/cl/import.go +++ b/cl/import.go @@ -172,10 +172,7 @@ func (p *context) initLinknameByDoc(doc *ast.CommentGroup, fullName, inPkgName s if n := len(doc.List); n > 0 { line := doc.List[n-1].Text p.initLinkname(line, func(name string) (_ string, _, ok bool) { - if name == inPkgName { - return fullName, isVar, true - } - return + return fullName, isVar, name == inPkgName }) } } From 879e4a0061e7592001dac4dd6cb8a4d4703a54f1 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 8 May 2024 18:57:14 +0800 Subject: [PATCH 8/9] build: support multiple link files in a package --- .gitignore | 1 + _demo/sqlite/sqlite.go | 32 +++--- c/c.go | 5 + cl/compile.go | 3 +- cl/import.go | 4 +- internal/build/build.go | 101 +++++++++++++++--- internal/llgen/llgenf.go | 15 ++- .../llvmlink/link.go} | 33 +++++- x/sqlite/llgo.cfg | 4 +- x/sqlite/llgo_autogen.lla | Bin 2291237 -> 2292074 bytes x/sqlite/sqlite.go | 8 +- 11 files changed, 164 insertions(+), 42 deletions(-) rename x/{gocmd/build_install_run.go => llvm/llvmlink/link.go} (57%) diff --git a/.gitignore b/.gitignore index 4d8f518b..d3ecf298 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.so *.dylib +test.db llgo_autogen.ll stories*.bin .DS_Store diff --git a/_demo/sqlite/sqlite.go b/_demo/sqlite/sqlite.go index bf353160..bce4f713 100644 --- a/_demo/sqlite/sqlite.go +++ b/_demo/sqlite/sqlite.go @@ -6,32 +6,34 @@ import ( ) func main() { - db, err := sqlite.OpenV2(c.Str(":memory:"), sqlite.OpenReadWrite|sqlite.OpenMemory, nil) - check(err) + c.Remove(c.Str("test.db")) + + db, err := sqlite.Open(c.Str("test.db")) + check(err, "sqlite: OpenV2") err = db.Exec(c.Str("CREATE TABLE foo (id INT, name TEXT)"), nil, nil, nil) - check(err) + check(err, "sqlite: Exec CREATE TABLE") - stmt, err := db.PrepareV3("INSERT INTO foo VALUES (?, ?)", 0, nil) - check(err) + stmt, err := db.PrepareV3("INSERT INTO foo (id, name) VALUES (?, ?)", 0, nil) + check(err, "sqlite: PrepareV3 INSERT") stmt.BindInt(1, 100) - stmt.BindText(2, c.Str("Hello World"), sqlite.Static, nil) + stmt.BindText(2, c.Str("Hello World"), -1, nil) err = stmt.Step() - checkDone(err) + checkDone(err, "sqlite: Step INSERT 1") stmt.Reset() stmt.BindInt(1, 200) - stmt.BindText(2, c.Str("This is llgo"), sqlite.Static, nil) + stmt.BindText(2, c.Str("This is llgo"), -1, nil) err = stmt.Step() - checkDone(err) + checkDone(err, "sqlite: Step INSERT 2") stmt.Close() stmt, err = db.PrepareV3("SELECT * FROM foo", 0, nil) - check(err) + check(err, "sqlite: PrepareV3 SELECT") for { if err = stmt.Step(); err != sqlite.HasRow { @@ -39,21 +41,21 @@ func main() { } c.Printf(c.Str("==> id=%d, name=%s\n"), stmt.ColumnInt(0), stmt.ColumnText(1)) } - checkDone(err) + checkDone(err, "sqlite: Step done") stmt.Close() db.Close() } -func check(err sqlite.Errno) { +func check(err sqlite.Errno, at string) { if err != sqlite.OK { - c.Printf(c.Str("==> Error: (%d) %s\n"), err, err.Errstr()) + c.Printf(c.Str("==> %s Error: (%d) %s\n"), c.AllocaCStr(at), err, err.Errstr()) c.Exit(1) } } -func checkDone(err sqlite.Errno) { +func checkDone(err sqlite.Errno, at string) { if err != sqlite.Done { - check(err) + check(err, at) } } diff --git a/c/c.go b/c/c.go index 49589a31..fb70ad1a 100644 --- a/c/c.go +++ b/c/c.go @@ -71,6 +71,11 @@ func GoStringData(string) *Char // ----------------------------------------------------------------------------- +//go:linkname Remove C.remove +func Remove(path *Char) Int + +// ----------------------------------------------------------------------------- + //go:linkname Exit C.exit func Exit(Int) diff --git a/cl/compile.go b/cl/compile.go index 24179d6d..92492463 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -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 { diff --git a/cl/import.go b/cl/import.go index 299e553c..a298eb6e 100644 --- a/cl/import.go +++ b/cl/import.go @@ -91,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": diff --git a/internal/build/build.go b/internal/build/build.go index 03d4ec06..3320e78d 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -167,16 +167,16 @@ func buildAllPkgs(prog llssa.Program, initial []*packages.Package, mode Mode, ve } for _, aPkg := range pkgs { pkg := aPkg.Package - switch cl.PkgKindOf(pkg.Types) { + switch kind := cl.PkgKindOf(pkg.Types); kind { case cl.PkgDeclOnly: // skip packages that only contain declarations // and set no export file pkg.ExportFile = "" - case cl.PkgLinkOnly: + case cl.PkgLinkIR: // cl.PkgLinkBitCode: // skip packages that don't need to be compiled but need to be linked pkgPath := pkg.PkgPath if isPkgInLLGo(pkgPath) { - pkg.ExportFile = strings.TrimSuffix(llgoPkgLinkFile(pkgPath), ".ll") + pkg.ExportFile = concatPkgLinkFiles(pkgPath) } else { panic("todo") } @@ -205,7 +205,7 @@ func linkMainPkg(pkg *packages.Package, pkgs []*aPackage, runtimeFiles []string, needRuntime := false packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) { if p.ExportFile != "" && !isRuntimePkg(p.PkgPath) { // skip packages that only contain declarations - args = append(args, p.ExportFile+".ll") + args = appendLinkFiles(args, p.ExportFile) if !needRuntime { needRuntime = isNeedRuntime(p) } @@ -219,8 +219,7 @@ func linkMainPkg(pkg *packages.Package, pkgs []*aPackage, runtimeFiles []string, lpkg := aPkg.LPkg lpkg.FuncOf(cl.RuntimeInit).MakeBody(1).Return() if needLLFile(mode) { - file := pkg.ExportFile + ".ll" - os.WriteFile(file, []byte(lpkg.String()), 0644) + os.WriteFile(pkg.ExportFile, []byte(lpkg.String()), 0644) } break } @@ -265,8 +264,8 @@ func buildPkg(prog llssa.Program, aPkg *aPackage, mode Mode, verbose bool) { ret, err := cl.NewPackage(prog, aPkg.SSA, pkg.Syntax) check(err) if needLLFile(mode) { - file := pkg.ExportFile + ".ll" - os.WriteFile(file, []byte(ret.String()), 0644) + pkg.ExportFile += ".ll" + os.WriteFile(pkg.ExportFile, []byte(ret.String()), 0644) } aPkg.LPkg = ret } @@ -358,8 +357,9 @@ func allLinkFiles(rt []*packages.Package) (outFiles []string) { packages.Visit(rt, nil, func(p *packages.Package) { pkgPath := p.PkgPath if isRuntimePkg(pkgPath) { - outFile := llgoPkgLinkFile(pkgPath) - outFiles = append(outFiles, outFile) + llgoPkgLinkFiles(pkgPath, func(linkFile string) { + outFiles = append(outFiles, linkFile) + }) } }) return @@ -393,12 +393,57 @@ func llgoRoot() string { return rootDir } -func llgoPkgLinkFile(pkgPath string) string { - llFile := filepath.Join(llgoRoot()+pkgPath[len(llgoModPath):], "llgo_autogen.ll") - if _, err := os.Stat(llFile); os.IsNotExist(err) { - decodeLinkFile(llFile) +func appendLinkFiles(args []string, file string) []string { + if isMultiLinkFiles(file) { + return append(args, strings.Split(file[1:], " ")...) + } + return append(args, file) +} + +func isMultiLinkFiles(ret string) bool { + return len(ret) > 0 && ret[0] == ' ' +} + +func concatPkgLinkFiles(pkgPath string) string { + var b strings.Builder + var ret string + var n int + llgoPkgLinkFiles(pkgPath, func(linkFile string) { + if n == 0 { + ret = linkFile + } else { + b.WriteByte(' ') + b.WriteString(linkFile) + } + n++ + }) + if n > 1 { + b.WriteByte(' ') + b.WriteString(ret) + return b.String() + } + return ret +} + +func llgoPkgLinkFiles(pkgPath string, procFile func(linkFile string)) { + dir := llgoRoot() + pkgPath[len(llgoModPath):] + "/" + llFile := dir + "llgo_autogen.ll" + llaFile := llFile + "a" + zipf, err := zip.OpenReader(llaFile) + if err != nil { + procFile(llFile) + return + } + defer zipf.Close() + + for _, f := range zipf.File { + procFile(dir + f.Name) + } + if _, err := os.Stat(llFile); os.IsNotExist(err) { + for _, f := range zipf.File { + decodeFile(dir+f.Name, f) + } } - return llFile } const ( @@ -417,6 +462,18 @@ func isPkgInMod(pkgPath, modPath string) bool { return false } +/* +func llgoPkgLinkFile(pkgPath string) string { + // if kind == cl.PkgLinkBitCode { + // return filepath.Join(llgoRoot()+pkgPath[len(llgoModPath):], "llgo_autogen.bc") + // } + llFile := filepath.Join(llgoRoot()+pkgPath[len(llgoModPath):], "llgo_autogen.ll") + if _, err := os.Stat(llFile); os.IsNotExist(err) { + decodeLinkFile(llFile) + } + return llFile +} + // *.ll => *.lla func decodeLinkFile(llFile string) { zipFile := llFile + "a" @@ -435,6 +492,20 @@ func decodeLinkFile(llFile string) { os.WriteFile(llFile, data, 0644) } } +*/ + +func decodeFile(outFile string, zipf *zip.File) (err error) { + f, err := zipf.Open() + if err != nil { + return + } + defer f.Close() + data, err := io.ReadAll(f) + if err == nil { + err = os.WriteFile(outFile, data, 0644) + } + return +} func check(err error) { if err != nil { diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go index 11a0645d..985ceaec 100644 --- a/internal/llgen/llgenf.go +++ b/internal/llgen/llgenf.go @@ -19,6 +19,7 @@ package llgen import ( "go/types" "os" + "os/exec" "path/filepath" "strings" @@ -74,10 +75,11 @@ func DoFile(fileOrPkg, outFile string) { } func SmartDoFile(inFile string, pkgPath ...string) { + const autgenFile = "llgo_autogen.ll" dir, _ := filepath.Split(inFile) absDir, _ := filepath.Abs(dir) absDir = filepath.ToSlash(absDir) - fname := "llgo_autogen.ll" + fname := autgenFile if inCompilerDir(absDir) { fname = "out.ll" } else if inSqlite(absDir) { @@ -90,6 +92,17 @@ func SmartDoFile(inFile string, pkgPath ...string) { } else { DoFile(inFile, outFile) } + if false && fname == autgenFile { + genZip(absDir, "llgo_autogen.lla", autgenFile) + } +} + +func genZip(dir string, outFile, inFile string) { + cmd := exec.Command("zip", outFile, inFile) + cmd.Dir = dir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Run() } func inCompilerDir(dir string) bool { diff --git a/x/gocmd/build_install_run.go b/x/llvm/llvmlink/link.go similarity index 57% rename from x/gocmd/build_install_run.go rename to x/llvm/llvmlink/link.go index fb046d75..3079affa 100644 --- a/x/gocmd/build_install_run.go +++ b/x/llvm/llvmlink/link.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved. + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,37 @@ * limitations under the License. */ -package gocmd +package llvmlink + +import ( + "io" + "os" + "os/exec" +) // ----------------------------------------------------------------------------- -type BuildConfig struct { - Output string +// Cmd represents a llvm-link command. +type Cmd struct { + app string + + Stdout io.Writer + Stderr io.Writer +} + +// New creates a new llvm-link command. +func New(app string) *Cmd { + if app == "" { + app = os.Getenv("LLGO_LLVM_ROOT") + "/bin/llvm-link" + } + return &Cmd{app, os.Stdout, os.Stderr} +} + +func (p *Cmd) Exec(args ...string) error { + cmd := exec.Command(p.app, args...) + cmd.Stdout = p.Stdout + cmd.Stderr = p.Stderr + return cmd.Run() } // ----------------------------------------------------------------------------- diff --git a/x/sqlite/llgo.cfg b/x/sqlite/llgo.cfg index 511ac2b5..8c24d7a7 100644 --- a/x/sqlite/llgo.cfg +++ b/x/sqlite/llgo.cfg @@ -4,7 +4,7 @@ "cd build.dir", "../sqlite/configure", "make", - "clang -emit-llvm -S -o sqlite3.ll -c sqlite3.c", - "$LLGO_LLVM_ROOT/bin/llvm-link -o ../llgo_autogen.bc ../sqlite.ll sqlite3.ll", + "clang -emit-llvm -S -o ../llgo_autogen.ll -c sqlite3.c", + "cd ..; rm llgo_autogen.lla; zip llgo_autogen.lla llgo_autogen.ll sqlite.ll", ] } diff --git a/x/sqlite/llgo_autogen.lla b/x/sqlite/llgo_autogen.lla index 7b8b4fadac0f547018808651175b3aeaaa89bdf0..e35d151bfce14b6935d2ad90f6451e5f52704544 100644 GIT binary patch delta 1025 zcmZ4brknBAr*1~x0B>d%5e5bZ4u;~E6%%ME zSz9gHS}oaIEjd~(Ia@8cS}nO-EqPikd0Q>{S}ploEd^RF1zRnJS}lcJEk#-_MO!V! zS}ny}EhSnlC0i|}S}mnpEoE9QWm_%fS}o;UEfrcV6}MU{HGi##IY236MTEk(+%ub* z7#Ms685lSjWEhGIb23X(^>T7TLpT|j`Pr@0wsBggl~!;wFtU7QWMBZh{cU)3_H6@+ z+VATH(iWcM@T~ECw2jB;g&do-w8*y&(-+)Wr5>Q-KkcyF%li2#H~aL@9`*B-G|}Jr zWA~i*b0*iHzoz@+)S)R0&X+%6-qE$;$JBMl8E^Y2iA^?{DIm1*WXrfJUH<} zW#7k;BptPK*$*sz{Qq>_z6czWZJ50DhX)6D-K=R!_vNpt1+F;dIU^@mv^f2Jnb=#g zBPq=Te$R6fJ}7(+|H*OTHG%w|>6N@&8M|&+2~o z!`j{ONrt-b_pQaAin~QGalBv9uQ za_rTmN0Hgbqt5Qyy7!LH=ef<=V&CI0J*q43*4In@`08J!@z4MHpRRG$f8P0RXZ5=C z-Rg|KL6_g#=HB}D`;F*dyL#LG`mgHdhyQse|7~~l_uo0s*UUwmM&`Ll~P=#;_4h|gMGr(F#CLi}HHme~EUdd0M~n9s{IlXbmNQ0CI5+#My)<~c|G zEt>Bdxbowhmzs-T2L#mhOH4hMofcE@HKZ^u=j9va<)0Spc>Gjh?$_OU*HyMpx^m(V zdw@41lN>WAJo`;Q`Blj{2$TWQ5;#O5L>*=>kpO021_oeeU|7<)>#GuSo?!*&nd$$( UDoOA$F)%SK0V>+?LkUO%0DRoBwEzGB delta 200 zcmX}mJx&5q07c>VnBhlp6ai5{K_;=VwmNKqhQ=+p0~1?fPDN#9O=0X!Y;0sb#KM?N zLg{EhZgOt>ZGM*V Date: Wed, 8 May 2024 23:19:49 +0800 Subject: [PATCH 9/9] demo --- _demo/sqlite/sqlite.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/_demo/sqlite/sqlite.go b/_demo/sqlite/sqlite.go index bce4f713..9dc4dc0f 100644 --- a/_demo/sqlite/sqlite.go +++ b/_demo/sqlite/sqlite.go @@ -9,31 +9,31 @@ func main() { c.Remove(c.Str("test.db")) db, err := sqlite.Open(c.Str("test.db")) - check(err, "sqlite: OpenV2") + check(err, db, "sqlite: Open") - err = db.Exec(c.Str("CREATE TABLE foo (id INT, name TEXT)"), nil, nil, nil) - check(err, "sqlite: Exec CREATE TABLE") + err = db.Exec(c.Str("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"), nil, nil, nil) + check(err, db, "sqlite: Exec CREATE TABLE") - stmt, err := db.PrepareV3("INSERT INTO foo (id, name) VALUES (?, ?)", 0, nil) - check(err, "sqlite: PrepareV3 INSERT") + stmt, err := db.PrepareV3("INSERT INTO users (id, name) VALUES (?, ?)", 0, nil) + check(err, db, "sqlite: PrepareV3 INSERT") stmt.BindInt(1, 100) stmt.BindText(2, c.Str("Hello World"), -1, nil) err = stmt.Step() - checkDone(err, "sqlite: Step INSERT 1") + checkDone(err, db, "sqlite: Step INSERT 1") stmt.Reset() stmt.BindInt(1, 200) stmt.BindText(2, c.Str("This is llgo"), -1, nil) err = stmt.Step() - checkDone(err, "sqlite: Step INSERT 2") + checkDone(err, db, "sqlite: Step INSERT 2") stmt.Close() - stmt, err = db.PrepareV3("SELECT * FROM foo", 0, nil) - check(err, "sqlite: PrepareV3 SELECT") + stmt, err = db.PrepareV3("SELECT * FROM users", 0, nil) + check(err, db, "sqlite: PrepareV3 SELECT") for { if err = stmt.Step(); err != sqlite.HasRow { @@ -41,21 +41,21 @@ func main() { } c.Printf(c.Str("==> id=%d, name=%s\n"), stmt.ColumnInt(0), stmt.ColumnText(1)) } - checkDone(err, "sqlite: Step done") + checkDone(err, db, "sqlite: Step done") stmt.Close() db.Close() } -func check(err sqlite.Errno, at string) { +func check(err sqlite.Errno, db *sqlite.Sqlite3, at string) { if err != sqlite.OK { - c.Printf(c.Str("==> %s Error: (%d) %s\n"), c.AllocaCStr(at), err, err.Errstr()) + c.Printf(c.Str("==> %s Error: (%d) %s\n"), c.AllocaCStr(at), err, db.Errmsg()) c.Exit(1) } } -func checkDone(err sqlite.Errno, at string) { +func checkDone(err sqlite.Errno, db *sqlite.Sqlite3, at string) { if err != sqlite.Done { - check(err, at) + check(err, db, at) } }