Compare commits

..

62 Commits

Author SHA1 Message Date
xushiwei
1463ed4835 Merge pull request #155 from xushiwei/q
retract v0.8.0
2024-05-13 01:01:25 +08:00
xushiwei
b959de18d5 retract v0.8.0 2024-05-13 01:00:54 +08:00
xushiwei
edac3f73c2 Merge pull request #154 from xushiwei/q
compileBlock bugfix: pyMod
2024-05-13 00:52:57 +08:00
xushiwei
388b19eed5 _testpy: sort by module name 2024-05-13 00:50:58 +08:00
xushiwei
a337136389 _testpy 2024-05-13 00:45:41 +08:00
xushiwei
31d5a8ac10 compileBlock bugfix: pyMod 2024-05-13 00:41:42 +08:00
xushiwei
46527f56ce Update README.md 2024-05-13 00:10:28 +08:00
xushiwei
a575c17eca Merge pull request #153 from xushiwei/q
README
2024-05-13 00:05:46 +08:00
xushiwei
a6ea3b1e6f README 2024-05-12 23:59:24 +08:00
xushiwei
4d2ad842a4 Merge pull request #152 from xushiwei/q
llgo/ssa: LoadPyModSyms; SetBlockEx AfterInit
2024-05-12 23:47:51 +08:00
xushiwei
ce0f5f3797 testpy: os.Getcwd 2024-05-12 23:45:56 +08:00
xushiwei
9e0d22ac90 x/sqlite/llgo_autogen.lla 2024-05-12 23:43:51 +08:00
xushiwei
ddc2c56115 gentests 2024-05-12 23:42:16 +08:00
xushiwei
acfbe6902a FuncDecl bugfix: even in C, we need to add ctx for method 2024-05-12 23:37:12 +08:00
xushiwei
f7dfab481b vkPyFunc => vkPyFuncRef 2024-05-12 23:08:44 +08:00
xushiwei
23692430d5 llgo/ssa: SetBlockEx AfterInit 2024-05-12 22:51:25 +08:00
xushiwei
03fe594339 compileBlock: use LoadPyModSyms 2024-05-12 20:03:27 +08:00
xushiwei
45babef689 compileBlock refactor 2024-05-12 18:42:45 +08:00
xushiwei
9ac0450255 llgo/ssa: LoadPyModSyms 2024-05-12 18:27:23 +08:00
xushiwei
b1d55f657d Merge pull request #151 from xushiwei/q
llgo/ssa: StringData/StringLen; cl: TestPython; build: PkgLinkExtern
2024-05-12 15:59:58 +08:00
xushiwei
aac5e7b3cd TestFromTestdata: print 2024-05-12 15:56:05 +08:00
xushiwei
791634c377 conflict fix 2024-05-12 15:52:29 +08:00
xushiwei
090149eab6 llgo/ssa: StringData/StringLen 2024-05-12 15:42:50 +08:00
xushiwei
acecbf587d cl: TestPython; build: PkgLinkExtern 2024-05-12 13:05:15 +08:00
xushiwei
4ef46971d0 Merge pull request #150 from xushiwei/q
TestFromTestpymath; llgo/ssa: NewPyFunc add param doInit
2024-05-12 12:18:09 +08:00
xushiwei
2e3cc49782 llgo/ssa: NewPyFunc add param doInit 2024-05-12 12:14:26 +08:00
xushiwei
8a0189b079 TestFromTestpymath 2024-05-12 11:51:46 +08:00
xushiwei
0edd7f6df0 Merge pull request #149 from xushiwei/q
PyInit
2024-05-12 11:28:04 +08:00
xushiwei
0b058bc2e8 test NewPyModVar 2024-05-12 11:26:09 +08:00
xushiwei
f9ef9cab81 TestSetBlockEx 2024-05-12 11:22:55 +08:00
xushiwei
fbb2150d88 TestFromTestpy 2024-05-12 11:13:04 +08:00
xushiwei
0912f1f509 PyInit 2024-05-12 11:11:19 +08:00
xushiwei
64c13fa9ae llgo/ssa: NewPyFunc fix 2024-05-12 00:24:56 +08:00
xushiwei
2a5f9d9641 Merge pull request #148 from xushiwei/q
_pydemo: callpy; PyFunction
2024-05-11 23:55:32 +08:00
xushiwei
6c32fe87e6 TestSetPython, TestPyFunc 2024-05-11 23:53:08 +08:00
xushiwei
09e9cc99d3 Merge pull request #147 from visualfc/typenamed
fix types named recursive
2024-05-11 23:42:06 +08:00
xushiwei
94a7ee024a llgo/ssa: pyCall; demo: _pydemo/callpy 2024-05-11 23:38:21 +08:00
xushiwei
a2d7a8c978 llgo/ssa: PyFunction; NewPyFunc 2024-05-11 21:55:50 +08:00
xushiwei
15499ddc14 cl: _testpy/math 2024-05-11 15:12:42 +08:00
xushiwei
97cb312386 llgo/ssa: b.NewPyModVar, b.ImportPyMod, PyObjectPtr, PyObjectPtrPtr 2024-05-11 15:07:50 +08:00
visualfc
92827a1f04 fix types named recursive 2024-05-11 13:34:08 +08:00
xushiwei
00222c7808 compileBlock: support pyModule init 2024-05-11 11:33:35 +08:00
xushiwei
427d87be68 cl: initPyModule 2024-05-11 10:07:46 +08:00
xushiwei
2560a333b6 FloatAsDouble => Float64 2024-05-11 06:55:57 +08:00
xushiwei
c6b76db789 Merge pull request #146 from xushiwei/q
LLGoPackage: PkgPyModule
2024-05-11 06:47:25 +08:00
xushiwei
1414853fce LLGoPackage: PkgPyModule 2024-05-11 06:44:45 +08:00
xushiwei
cbe384a3be Merge pull request #145 from xushiwei/q
pyimport & deomo: callpy (todo)
2024-05-11 06:25:35 +08:00
xushiwei
cca46573ea pyimport & deomo: callpy (todo) 2024-05-11 06:23:05 +08:00
xushiwei
2589c23998 Merge pull request #144 from xushiwei/q
nmindex: query auto add _ prefix
2024-05-11 05:45:26 +08:00
xushiwei
29830865d9 nmindex: query auto add _ prefix 2024-05-11 05:43:27 +08:00
xushiwei
35dbc7b0b5 Merge pull request #143 from xushiwei/q
mv x/<tool> => xtool/<tool>
2024-05-11 05:29:38 +08:00
xushiwei
cd266213ce mv x/<tool> => xtool/<tool> 2024-05-11 05:27:38 +08:00
xushiwei
875eadeb19 Merge pull request #142 from xushiwei/q
py/README
2024-05-11 05:18:41 +08:00
xushiwei
67896c63a7 py/README 2024-05-11 05:18:17 +08:00
xushiwei
c643f734e9 Merge pull request #141 from xushiwei/q
move python demos to _pydemo
2024-05-11 05:05:07 +08:00
xushiwei
31d91f5e87 move python demos to _pydemo 2024-05-11 05:04:02 +08:00
xushiwei
2a92f1bc55 Merge pull request #140 from xushiwei/q
py demo: hellpy, clpy, callpy
2024-05-11 04:31:40 +08:00
xushiwei
d9db5528f5 py demo: hellpy, clpy, callpy 2024-05-11 04:26:41 +08:00
xushiwei
22a4ffb78b Merge pull request #139 from visualfc/iface
typeAssert: bool float string
2024-05-11 02:49:31 +08:00
visualfc
1fb37c37fe binop: token.AND_NOT 2024-05-10 21:34:54 +08:00
visualfc
aae663e5e5 binop: bool eql/neq 2024-05-10 21:06:19 +08:00
visualfc
e985eda857 typeAssert: bool float string 2024-05-10 13:46:39 +08:00
80 changed files with 2330 additions and 455 deletions

1
.gitignore vendored
View File

@@ -18,6 +18,7 @@ _go/
_runtime/ _runtime/
_tinygo/ _tinygo/
build.dir/ build.dir/
.vscode/
# Test binary, built with `go test -c` # Test binary, built with `go test -c`
*.test *.test

View File

@@ -8,7 +8,51 @@ llgo - A Go compiler based on LLVM
[![GoDoc](https://pkg.go.dev/badge/github.com/goplus/llgo.svg)](https://pkg.go.dev/github.com/goplus/llgo) [![GoDoc](https://pkg.go.dev/badge/github.com/goplus/llgo.svg)](https://pkg.go.dev/github.com/goplus/llgo)
[![Language](https://img.shields.io/badge/language-Go+-blue.svg)](https://github.com/goplus/gop) [![Language](https://img.shields.io/badge/language-Go+-blue.svg)](https://github.com/goplus/gop)
This is a Go compiler based on LLVM in order to better integrate Go with the C ecosystem. It's a subproject of [the Go+ project](https://github.com/goplus/gop). This is a Go compiler based on LLVM in order to better integrate Go with the C ecosystem including Python. It's a subproject of [the Go+ project](https://github.com/goplus/gop).
## C standard libary support
```go
package main
import "github.com/goplus/llgo/c"
func main() {
c.Printf(c.Str("Hello world\n"))
}
```
This is a simple example of calling the C `printf` function to print `Hello world`. Here, `c.Str` is not a function for converting a Go string to a C string, but a built-in instruction supported by llgo for generating a C string constant.
See [github.com/goplus/llgo/c](https://pkg.go.dev/github.com/goplus/llgo/c) for more detials.
## Python support
You can import a Python library in llgo! For example:
```go
package main
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/py"
"github.com/goplus/llgo/py/math"
)
func main() {
x := math.Sqrt(py.Float(2))
c.Printf(c.Str("sqrt(2) = %f\n"), x.Float64())
}
```
Here, We call `py.Float(2)` to create a Python floating point number 2, and pass it to Pythons `math.sqrt` to get `x`. Then use `x.Float64()` to convert the Python object to Go's `float64` type, and finally we print the value through C `printf`.
## Other frequently used libraries
TODO
## How to install ## How to install
@@ -46,11 +90,36 @@ The `_demo` directory contains our demos (it start with `_` to prevent the `go`
* [qsort](_demo/qsort/qsort.go): call C function with a callback (eg. qsort) * [qsort](_demo/qsort/qsort.go): call C function with a callback (eg. qsort)
* [genints](_demo/genints/genints.go): various forms of closure usage (including C function, recv.method and anonymous function) * [genints](_demo/genints/genints.go): various forms of closure usage (including C function, recv.method and anonymous function)
* [llama2-c](_demo/llama2-c): inference Llama 2 (It's the first llgo AI example) * [llama2-c](_demo/llama2-c): inference Llama 2 (It's the first llgo AI example)
* [hellopy](https://github.com/goplus/cpython/blob/main/_demo/hellopy/hello.go): link Python to Go and say `Hello world`
And the `_pydemo` directory contains python related demos:
* [callpy](_pydemo/callpy/callpy.go): call Python standard library function `math.sqrt`
### How to run demos ### How to run demos
To run the demos in directory `_demo`:
```sh ```sh
cd <demo-directory> # eg. cd _demo/genints cd <demo-directory> # eg. cd _demo/genints
llgo run . llgo run .
``` ```
To run the demos in directory `_pydemo`, you need to set the `LLGO_LIB_PYTHON` environment variable first. Assuming you use Python 3.12, and the `libpython3.12.so` (or `libpython3.12.dylib` or `python3.12.lib`) file is in the /foo/bar directory, then you need to set `LLGO_LIB_PYTHON` to:
```sh
export LLGO_LIB_PYTHON=/foo/bar/python3.12
```
For example, `/opt/homebrew/Frameworks/Python.framework/Versions/3.12/libpython3.12.dylib` is a typical python lib location under macOS. So we should set it like this:
```sh
export LLGO_LIB_PYTHON=/opt/homebrew/Frameworks/Python.framework/Versions/3.12/python3.12
```
Then you can run the demos in directory `_pydemo`:
```sh
cd <demo-directory> # eg. cd _pydemo/callpy
llgo run .
```

12
_pydemo/callpy/callpy.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/py"
"github.com/goplus/llgo/py/math"
)
func main() {
x := math.Sqrt(py.Float(2))
c.Printf(c.Str("sqrt(2) = %f\n"), x.Float64())
}

2
c/c.go
View File

@@ -66,7 +66,7 @@ func Memset(s Pointer, c Int, n uintptr) Pointer
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//go:linkname GoStringData github.com/goplus/llgo/internal/runtime.StringData //go:linkname GoStringData llgo.stringData
func GoStringData(string) *Char func GoStringData(string) *Char
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -22,7 +22,7 @@ import (
"log" "log"
"os" "os"
"github.com/goplus/llgo/x/ar" "github.com/goplus/llgo/xtool/ar"
) )
func main() { func main() {

View File

@@ -22,7 +22,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/goplus/llgo/x/clang/parser" "github.com/goplus/llgo/xtool/clang/parser"
) )
var ( var (

View File

@@ -20,7 +20,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/goplus/llgo/x/clang/preprocessor" "github.com/goplus/llgo/xtool/clang/preprocessor"
) )
func usage() { func usage() {

View File

@@ -33,6 +33,7 @@ func main() {
llgenDir(dir + "/cl/_testlibc") llgenDir(dir + "/cl/_testlibc")
llgenDir(dir + "/cl/_testrt") llgenDir(dir + "/cl/_testrt")
llgenDir(dir+"/cl/_testpy", "")
llgenDir(dir+"/cl/_testdata", "") llgenDir(dir+"/cl/_testdata", "")
} }

View File

@@ -21,7 +21,7 @@ import (
"log" "log"
"os" "os"
"github.com/goplus/llgo/x/env/llvm" "github.com/goplus/llgo/xtool/env/llvm"
) )
func main() { func main() {

View File

@@ -20,8 +20,8 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/goplus/llgo/x/env/llvm" "github.com/goplus/llgo/xtool/env/llvm"
"github.com/goplus/llgo/x/nm" "github.com/goplus/llgo/xtool/nm"
) )
func main() { func main() {
@@ -73,6 +73,11 @@ func makeIndex() {
} }
func query(q string) { func query(q string) {
if len(q) > 0 {
if c := q[0]; c != '*' && c != '_' {
q = "_" + q
}
}
files, err := nm.Query(indexDir(), q) files, err := nm.Query(indexDir(), q)
check(err) check(err)
for _, f := range files { for _, f := range files {

View File

@@ -13,6 +13,14 @@ func gwrite(b []byte) {
c.Printf(c.Str("%s"), b) c.Printf(c.Str("%s"), b)
} }
func printbool(v bool) {
if v {
printstring("true")
} else {
printstring("false")
}
}
func printfloat(v float64) { func printfloat(v float64) {
switch { switch {
case v != v: case v != v:
@@ -177,14 +185,37 @@ func main() {
printnl() printnl()
prinfsub(100.1) prinfsub(100.1)
printnl() printnl()
printnum(float32(1e9)) printany(float32(1e9))
printnl() printnl()
printnum(float64(2e9)) printany(float64(2e9))
printnl()
var b bool = true
if b == true && b != false {
println("check bool", b)
}
n1 := 0b1001
n2 := 0b0011
println("check &^", n1&^n2 == 0b1000, n2&^n1 == 0b0010)
println(true, false, 'a', 'A', rune('中'),
int8(1), int16(2), int32(3), int64(4), 5,
uint8(1), uint16(2), uint32(3), uint64(4), uintptr(5),
"llgo")
}
func println(args ...any) {
for i, v := range args {
if i != 0 {
printstring(" ")
}
printany(v)
}
printnl() printnl()
} }
func printnum(v any) { func printany(v any) {
switch v := v.(type) { switch v := v.(type) {
case bool:
printbool(v)
case int: case int:
printint(int64(v)) printint(int64(v))
case int8: case int8:
@@ -211,6 +242,8 @@ func printnum(v any) {
printfloat(float64(v)) printfloat(float64(v))
case float64: case float64:
printfloat(float64(v)) printfloat(float64(v))
case string:
printstring(v)
} }
} }

View File

@@ -13,13 +13,19 @@ source_filename = "main"
@__llgo_argc = global ptr null @__llgo_argc = global ptr null
@__llgo_argv = global ptr null @__llgo_argv = global ptr null
@1 = private unnamed_addr constant [5 x i8] c"llgo\00", align 1 @1 = private unnamed_addr constant [5 x i8] c"llgo\00", align 1
@2 = private unnamed_addr constant [4 x i8] c"NaN\00", align 1 @2 = private unnamed_addr constant [11 x i8] c"check bool\00", align 1
@3 = private unnamed_addr constant [5 x i8] c"+Inf\00", align 1 @3 = private unnamed_addr constant [9 x i8] c"check &^\00", align 1
@4 = private unnamed_addr constant [5 x i8] c"-Inf\00", align 1 @4 = private unnamed_addr constant [5 x i8] c"llgo\00", align 1
@5 = private unnamed_addr constant [17 x i8] c"0123456789abcdef\00", align 1 @5 = private unnamed_addr constant [5 x i8] c"true\00", align 1
@6 = private unnamed_addr constant [2 x i8] c"-\00", align 1 @6 = private unnamed_addr constant [6 x i8] c"false\00", align 1
@7 = private unnamed_addr constant [2 x i8] c"\0A\00", align 1 @7 = private unnamed_addr constant [4 x i8] c"NaN\00", align 1
@8 = private unnamed_addr constant [2 x i8] c" \00", align 1 @8 = private unnamed_addr constant [5 x i8] c"+Inf\00", align 1
@9 = private unnamed_addr constant [5 x i8] c"-Inf\00", align 1
@10 = private unnamed_addr constant [17 x i8] c"0123456789abcdef\00", align 1
@11 = private unnamed_addr constant [2 x i8] c"-\00", align 1
@12 = private unnamed_addr constant [2 x i8] c" \00", align 1
@13 = private unnamed_addr constant [2 x i8] c"\0A\00", align 1
@14 = private unnamed_addr constant [2 x i8] c" \00", align 1
define %"github.com/goplus/llgo/internal/runtime.Slice" @main.bytes(%"github.com/goplus/llgo/internal/runtime.String" %0) { define %"github.com/goplus/llgo/internal/runtime.Slice" @main.bytes(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0: _llgo_0:
@@ -94,13 +100,115 @@ _llgo_0:
call void @main.printnl() call void @main.printnl()
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 13) %3 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 13)
%4 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %3, i64 1315859240) %4 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %3, i64 1315859240)
call void @main.printnum(%"github.com/goplus/llgo/internal/runtime.iface" %4) call void @main.printany(%"github.com/goplus/llgo/internal/runtime.iface" %4)
call void @main.printnl() call void @main.printnl()
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 14) %5 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 14)
%6 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %5, i64 4746175415993761792) %6 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %5, i64 4746175415993761792)
call void @main.printnum(%"github.com/goplus/llgo/internal/runtime.iface" %6) call void @main.printany(%"github.com/goplus/llgo/internal/runtime.iface" %6)
call void @main.printnl() call void @main.printnl()
br i1 true, label %_llgo_3, label %_llgo_2
_llgo_1: ; preds = %_llgo_3
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %7, i64 0
%9 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 10)
%10 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %9)
store %"github.com/goplus/llgo/internal/runtime.iface" %10, ptr %8, align 8
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %7, i64 1
%12 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 1)
%13 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %12, i64 -1)
store %"github.com/goplus/llgo/internal/runtime.iface" %13, ptr %11, align 8
%14 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %7, i64 16, i64 2, i64 0, i64 2, i64 2)
call void @main.println(%"github.com/goplus/llgo/internal/runtime.Slice" %14)
br label %_llgo_2
_llgo_2: ; preds = %_llgo_3, %_llgo_1, %_llgo_0
%15 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 48)
%16 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %15, i64 0
%17 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 8)
%18 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %17)
store %"github.com/goplus/llgo/internal/runtime.iface" %18, ptr %16, align 8
%19 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %15, i64 1
%20 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 1)
%21 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %20, i64 -1)
store %"github.com/goplus/llgo/internal/runtime.iface" %21, ptr %19, align 8
%22 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %15, i64 2
%23 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 1)
%24 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %23, i64 -1)
store %"github.com/goplus/llgo/internal/runtime.iface" %24, ptr %22, align 8
%25 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %15, i64 16, i64 3, i64 0, i64 3, i64 3)
call void @main.println(%"github.com/goplus/llgo/internal/runtime.Slice" %25)
%26 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 256)
%27 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 0
%28 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 1)
%29 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %28, i64 -1)
store %"github.com/goplus/llgo/internal/runtime.iface" %29, ptr %27, align 8
%30 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 1
%31 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 1)
%32 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %31, i64 0)
store %"github.com/goplus/llgo/internal/runtime.iface" %32, ptr %30, align 8
%33 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 2
%34 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 5)
%35 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %34, i64 97)
store %"github.com/goplus/llgo/internal/runtime.iface" %35, ptr %33, align 8
%36 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 3
%37 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 5)
%38 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %37, i64 65)
store %"github.com/goplus/llgo/internal/runtime.iface" %38, ptr %36, align 8
%39 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 4
%40 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 5)
%41 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %40, i64 20013)
store %"github.com/goplus/llgo/internal/runtime.iface" %41, ptr %39, align 8
%42 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 5
%43 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 3)
%44 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %43, i64 1)
store %"github.com/goplus/llgo/internal/runtime.iface" %44, ptr %42, align 8
%45 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 6
%46 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 4)
%47 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %46, i64 2)
store %"github.com/goplus/llgo/internal/runtime.iface" %47, ptr %45, align 8
%48 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 7
%49 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 5)
%50 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %49, i64 3)
store %"github.com/goplus/llgo/internal/runtime.iface" %50, ptr %48, align 8
%51 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 8
%52 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 6)
%53 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %52, i64 4)
store %"github.com/goplus/llgo/internal/runtime.iface" %53, ptr %51, align 8
%54 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 9
%55 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2)
%56 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %55, i64 5)
store %"github.com/goplus/llgo/internal/runtime.iface" %56, ptr %54, align 8
%57 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 10
%58 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 8)
%59 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %58, i64 1)
store %"github.com/goplus/llgo/internal/runtime.iface" %59, ptr %57, align 8
%60 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 11
%61 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 9)
%62 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %61, i64 2)
store %"github.com/goplus/llgo/internal/runtime.iface" %62, ptr %60, align 8
%63 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 12
%64 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 10)
%65 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %64, i64 3)
store %"github.com/goplus/llgo/internal/runtime.iface" %65, ptr %63, align 8
%66 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 13
%67 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 11)
%68 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %67, i64 4)
store %"github.com/goplus/llgo/internal/runtime.iface" %68, ptr %66, align 8
%69 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 14
%70 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 12)
%71 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %70, i64 5)
store %"github.com/goplus/llgo/internal/runtime.iface" %71, ptr %69, align 8
%72 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %26, i64 15
%73 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @4, i64 4)
%74 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %73)
store %"github.com/goplus/llgo/internal/runtime.iface" %74, ptr %72, align 8
%75 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %26, i64 16, i64 16, i64 0, i64 16, i64 16)
call void @main.println(%"github.com/goplus/llgo/internal/runtime.Slice" %75)
ret void ret void
_llgo_3: ; preds = %_llgo_0
br i1 true, label %_llgo_1, label %_llgo_2
} }
define void @main.prinfsub(double %0) { define void @main.prinfsub(double %0) {
@@ -117,18 +225,223 @@ _llgo_0:
ret void ret void
} }
define void @main.printany(%"github.com/goplus/llgo/internal/runtime.iface" %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 1)
%2 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1)
%3 = extractvalue { i64, i1 } %2, 0
%4 = trunc i64 %3 to i1
%5 = extractvalue { i64, i1 } %2, 1
br i1 %5, label %_llgo_2, label %_llgo_3
_llgo_1: ; preds = %_llgo_30, %_llgo_29, %_llgo_28, %_llgo_26, %_llgo_24, %_llgo_22, %_llgo_20, %_llgo_18, %_llgo_16, %_llgo_14, %_llgo_12, %_llgo_10, %_llgo_8, %_llgo_6, %_llgo_4, %_llgo_2
ret void
_llgo_2: ; preds = %_llgo_0
call void @main.printbool(i1 %4)
br label %_llgo_1
_llgo_3: ; preds = %_llgo_0
%6 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2)
%7 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %6)
%8 = extractvalue { i64, i1 } %7, 0
%9 = extractvalue { i64, i1 } %7, 1
br i1 %9, label %_llgo_4, label %_llgo_5
_llgo_4: ; preds = %_llgo_3
call void @main.printint(i64 %8)
br label %_llgo_1
_llgo_5: ; preds = %_llgo_3
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 3)
%11 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %10)
%12 = extractvalue { i64, i1 } %11, 0
%13 = trunc i64 %12 to i8
%14 = extractvalue { i64, i1 } %11, 1
br i1 %14, label %_llgo_6, label %_llgo_7
_llgo_6: ; preds = %_llgo_5
%15 = sext i8 %13 to i64
call void @main.printint(i64 %15)
br label %_llgo_1
_llgo_7: ; preds = %_llgo_5
%16 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 4)
%17 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %16)
%18 = extractvalue { i64, i1 } %17, 0
%19 = trunc i64 %18 to i16
%20 = extractvalue { i64, i1 } %17, 1
br i1 %20, label %_llgo_8, label %_llgo_9
_llgo_8: ; preds = %_llgo_7
%21 = sext i16 %19 to i64
call void @main.printint(i64 %21)
br label %_llgo_1
_llgo_9: ; preds = %_llgo_7
%22 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 5)
%23 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %22)
%24 = extractvalue { i64, i1 } %23, 0
%25 = trunc i64 %24 to i32
%26 = extractvalue { i64, i1 } %23, 1
br i1 %26, label %_llgo_10, label %_llgo_11
_llgo_10: ; preds = %_llgo_9
%27 = sext i32 %25 to i64
call void @main.printint(i64 %27)
br label %_llgo_1
_llgo_11: ; preds = %_llgo_9
%28 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 6)
%29 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %28)
%30 = extractvalue { i64, i1 } %29, 0
%31 = extractvalue { i64, i1 } %29, 1
br i1 %31, label %_llgo_12, label %_llgo_13
_llgo_12: ; preds = %_llgo_11
call void @main.printint(i64 %30)
br label %_llgo_1
_llgo_13: ; preds = %_llgo_11
%32 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 7)
%33 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %32)
%34 = extractvalue { i64, i1 } %33, 0
%35 = extractvalue { i64, i1 } %33, 1
br i1 %35, label %_llgo_14, label %_llgo_15
_llgo_14: ; preds = %_llgo_13
call void @main.printuint(i64 %34)
br label %_llgo_1
_llgo_15: ; preds = %_llgo_13
%36 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 8)
%37 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %36)
%38 = extractvalue { i64, i1 } %37, 0
%39 = trunc i64 %38 to i8
%40 = extractvalue { i64, i1 } %37, 1
br i1 %40, label %_llgo_16, label %_llgo_17
_llgo_16: ; preds = %_llgo_15
%41 = sext i8 %39 to i64
call void @main.printuint(i64 %41)
br label %_llgo_1
_llgo_17: ; preds = %_llgo_15
%42 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 9)
%43 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %42)
%44 = extractvalue { i64, i1 } %43, 0
%45 = trunc i64 %44 to i16
%46 = extractvalue { i64, i1 } %43, 1
br i1 %46, label %_llgo_18, label %_llgo_19
_llgo_18: ; preds = %_llgo_17
%47 = sext i16 %45 to i64
call void @main.printuint(i64 %47)
br label %_llgo_1
_llgo_19: ; preds = %_llgo_17
%48 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 10)
%49 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %48)
%50 = extractvalue { i64, i1 } %49, 0
%51 = trunc i64 %50 to i32
%52 = extractvalue { i64, i1 } %49, 1
br i1 %52, label %_llgo_20, label %_llgo_21
_llgo_20: ; preds = %_llgo_19
%53 = sext i32 %51 to i64
call void @main.printuint(i64 %53)
br label %_llgo_1
_llgo_21: ; preds = %_llgo_19
%54 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 11)
%55 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %54)
%56 = extractvalue { i64, i1 } %55, 0
%57 = extractvalue { i64, i1 } %55, 1
br i1 %57, label %_llgo_22, label %_llgo_23
_llgo_22: ; preds = %_llgo_21
call void @main.printuint(i64 %56)
br label %_llgo_1
_llgo_23: ; preds = %_llgo_21
%58 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 12)
%59 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %58)
%60 = extractvalue { i64, i1 } %59, 0
%61 = extractvalue { i64, i1 } %59, 1
br i1 %61, label %_llgo_24, label %_llgo_25
_llgo_24: ; preds = %_llgo_23
call void @main.printuint(i64 %60)
br label %_llgo_1
_llgo_25: ; preds = %_llgo_23
%62 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 13)
%63 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %62)
%64 = extractvalue { i64, i1 } %63, 0
%65 = trunc i64 %64 to i32
%66 = bitcast i32 %65 to float
%67 = extractvalue { i64, i1 } %63, 1
br i1 %67, label %_llgo_26, label %_llgo_27
_llgo_26: ; preds = %_llgo_25
%68 = fpext float %66 to double
call void @main.printfloat(double %68)
br label %_llgo_1
_llgo_27: ; preds = %_llgo_25
%69 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 14)
%70 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %69)
%71 = extractvalue { i64, i1 } %70, 0
%72 = bitcast i64 %71 to double
%73 = extractvalue { i64, i1 } %70, 1
br i1 %73, label %_llgo_28, label %_llgo_29
_llgo_28: ; preds = %_llgo_27
call void @main.printfloat(double %72)
br label %_llgo_1
_llgo_29: ; preds = %_llgo_27
%74 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 24)
%75 = call { %"github.com/goplus/llgo/internal/runtime.String", i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2String"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %74)
%76 = extractvalue { %"github.com/goplus/llgo/internal/runtime.String", i1 } %75, 0
%77 = extractvalue { %"github.com/goplus/llgo/internal/runtime.String", i1 } %75, 1
br i1 %77, label %_llgo_30, label %_llgo_1
_llgo_30: ; preds = %_llgo_29
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %76)
br label %_llgo_1
}
define void @main.printbool(i1 %0) {
_llgo_0:
br i1 %0, label %_llgo_1, label %_llgo_3
_llgo_1: ; preds = %_llgo_0
%1 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @5, i64 4)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %1)
br label %_llgo_2
_llgo_2: ; preds = %_llgo_3, %_llgo_1
ret void
_llgo_3: ; preds = %_llgo_0
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @6, i64 5)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %2)
br label %_llgo_2
}
define void @main.printfloat(double %0) { define void @main.printfloat(double %0) {
_llgo_0: _llgo_0:
%1 = fcmp one double %0, %0 %1 = fcmp one double %0, %0
br i1 %1, label %_llgo_1, label %_llgo_3 br i1 %1, label %_llgo_1, label %_llgo_3
_llgo_1: ; preds = %_llgo_0 _llgo_1: ; preds = %_llgo_0
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 3) %2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @7, i64 3)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %2) call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %2)
ret void ret void
_llgo_2: ; preds = %_llgo_7 _llgo_2: ; preds = %_llgo_7
%3 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 4) %3 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @8, i64 4)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %3) call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %3)
ret void ret void
@@ -138,7 +451,7 @@ _llgo_3: ; preds = %_llgo_0
br i1 %5, label %_llgo_6, label %_llgo_7 br i1 %5, label %_llgo_6, label %_llgo_7
_llgo_4: ; preds = %_llgo_10 _llgo_4: ; preds = %_llgo_10
%6 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @4, i64 4) %6 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @9, i64 4)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %6) call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %6)
ret void ret void
@@ -310,8 +623,8 @@ _llgo_0:
_llgo_1: ; preds = %_llgo_3 _llgo_1: ; preds = %_llgo_3
%2 = urem i64 %14, 16 %2 = urem i64 %14, 16
%3 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @5, i64 16) %3 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @10, i64 16)
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %3) %4 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %3, 0
%5 = getelementptr inbounds i8, ptr %4, i64 %2 %5 = getelementptr inbounds i8, ptr %4, i64 %2
%6 = load i8, ptr %5, align 1 %6 = load i8, ptr %5, align 1
%7 = getelementptr inbounds i8, ptr %1, i64 %15 %7 = getelementptr inbounds i8, ptr %1, i64 %15
@@ -354,7 +667,7 @@ _llgo_0:
br i1 %1, label %_llgo_1, label %_llgo_2 br i1 %1, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0 _llgo_1: ; preds = %_llgo_0
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @6, i64 1) %2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @11, i64 1)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %2) call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %2)
%3 = sub i64 0, %0 %3 = sub i64 0, %0
br label %_llgo_2 br label %_llgo_2
@@ -365,165 +678,48 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void ret void
} }
define void @main.println(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
br label %_llgo_1
_llgo_1: ; preds = %_llgo_5, %_llgo_0
%2 = phi i64 [ -1, %_llgo_0 ], [ %3, %_llgo_5 ]
%3 = add i64 %2, 1
%4 = icmp slt i64 %3, %1
br i1 %4, label %_llgo_2, label %_llgo_3
_llgo_2: ; preds = %_llgo_1
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %5, i64 %3
%7 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %6, align 8
%8 = icmp ne i64 %3, 0
br i1 %8, label %_llgo_4, label %_llgo_5
_llgo_3: ; preds = %_llgo_1
call void @main.printnl()
ret void
_llgo_4: ; preds = %_llgo_2
%9 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @12, i64 1)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %9)
br label %_llgo_5
_llgo_5: ; preds = %_llgo_4, %_llgo_2
call void @main.printany(%"github.com/goplus/llgo/internal/runtime.iface" %7)
br label %_llgo_1
}
define void @main.printnl() { define void @main.printnl() {
_llgo_0: _llgo_0:
%0 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @7, i64 1) %0 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @13, i64 1)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %0) call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %0)
ret void ret void
} }
define void @main.printnum(%"github.com/goplus/llgo/internal/runtime.iface" %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2)
%2 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1)
%3 = extractvalue { i64, i1 } %2, 0
%4 = extractvalue { i64, i1 } %2, 1
br i1 %4, label %_llgo_2, label %_llgo_3
_llgo_1: ; preds = %_llgo_26, %_llgo_25, %_llgo_24, %_llgo_22, %_llgo_20, %_llgo_18, %_llgo_16, %_llgo_14, %_llgo_12, %_llgo_10, %_llgo_8, %_llgo_6, %_llgo_4, %_llgo_2
ret void
_llgo_2: ; preds = %_llgo_0
call void @main.printint(i64 %3)
br label %_llgo_1
_llgo_3: ; preds = %_llgo_0
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 3)
%6 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %5)
%7 = extractvalue { i64, i1 } %6, 0
%8 = extractvalue { i64, i1 } %6, 1
br i1 %8, label %_llgo_4, label %_llgo_5
_llgo_4: ; preds = %_llgo_3
call void @main.printint(i64 %7)
br label %_llgo_1
_llgo_5: ; preds = %_llgo_3
%9 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 4)
%10 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %9)
%11 = extractvalue { i64, i1 } %10, 0
%12 = extractvalue { i64, i1 } %10, 1
br i1 %12, label %_llgo_6, label %_llgo_7
_llgo_6: ; preds = %_llgo_5
call void @main.printint(i64 %11)
br label %_llgo_1
_llgo_7: ; preds = %_llgo_5
%13 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 5)
%14 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %13)
%15 = extractvalue { i64, i1 } %14, 0
%16 = extractvalue { i64, i1 } %14, 1
br i1 %16, label %_llgo_8, label %_llgo_9
_llgo_8: ; preds = %_llgo_7
call void @main.printint(i64 %15)
br label %_llgo_1
_llgo_9: ; preds = %_llgo_7
%17 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 6)
%18 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %17)
%19 = extractvalue { i64, i1 } %18, 0
%20 = extractvalue { i64, i1 } %18, 1
br i1 %20, label %_llgo_10, label %_llgo_11
_llgo_10: ; preds = %_llgo_9
call void @main.printint(i64 %19)
br label %_llgo_1
_llgo_11: ; preds = %_llgo_9
%21 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 7)
%22 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %21)
%23 = extractvalue { i64, i1 } %22, 0
%24 = extractvalue { i64, i1 } %22, 1
br i1 %24, label %_llgo_12, label %_llgo_13
_llgo_12: ; preds = %_llgo_11
call void @main.printuint(i64 %23)
br label %_llgo_1
_llgo_13: ; preds = %_llgo_11
%25 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 8)
%26 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %25)
%27 = extractvalue { i64, i1 } %26, 0
%28 = extractvalue { i64, i1 } %26, 1
br i1 %28, label %_llgo_14, label %_llgo_15
_llgo_14: ; preds = %_llgo_13
call void @main.printuint(i64 %27)
br label %_llgo_1
_llgo_15: ; preds = %_llgo_13
%29 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 9)
%30 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %29)
%31 = extractvalue { i64, i1 } %30, 0
%32 = extractvalue { i64, i1 } %30, 1
br i1 %32, label %_llgo_16, label %_llgo_17
_llgo_16: ; preds = %_llgo_15
call void @main.printuint(i64 %31)
br label %_llgo_1
_llgo_17: ; preds = %_llgo_15
%33 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 10)
%34 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %33)
%35 = extractvalue { i64, i1 } %34, 0
%36 = extractvalue { i64, i1 } %34, 1
br i1 %36, label %_llgo_18, label %_llgo_19
_llgo_18: ; preds = %_llgo_17
call void @main.printuint(i64 %35)
br label %_llgo_1
_llgo_19: ; preds = %_llgo_17
%37 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 11)
%38 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %37)
%39 = extractvalue { i64, i1 } %38, 0
%40 = extractvalue { i64, i1 } %38, 1
br i1 %40, label %_llgo_20, label %_llgo_21
_llgo_20: ; preds = %_llgo_19
call void @main.printuint(i64 %39)
br label %_llgo_1
_llgo_21: ; preds = %_llgo_19
%41 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 12)
%42 = call { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %41)
%43 = extractvalue { i64, i1 } %42, 0
%44 = extractvalue { i64, i1 } %42, 1
br i1 %44, label %_llgo_22, label %_llgo_23
_llgo_22: ; preds = %_llgo_21
call void @main.printuint(i64 %43)
br label %_llgo_1
_llgo_23: ; preds = %_llgo_21
%45 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 13)
%46 = call { float, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Float32"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %45)
%47 = extractvalue { float, i1 } %46, 0
%48 = extractvalue { float, i1 } %46, 1
br i1 %48, label %_llgo_24, label %_llgo_25
_llgo_24: ; preds = %_llgo_23
%49 = fpext float %47 to double
call void @main.printfloat(double %49)
br label %_llgo_1
_llgo_25: ; preds = %_llgo_23
%50 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 14)
%51 = call { double, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Float64"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %50)
%52 = extractvalue { double, i1 } %51, 0
%53 = extractvalue { double, i1 } %51, 1
br i1 %53, label %_llgo_26, label %_llgo_1
_llgo_26: ; preds = %_llgo_25
call void @main.printfloat(double %52)
br label %_llgo_1
}
define void @main.printsp() { define void @main.printsp() {
_llgo_0: _llgo_0:
%0 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @8, i64 1) %0 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @14, i64 1)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %0) call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %0)
ret void ret void
} }
@@ -599,12 +795,12 @@ declare ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64)
declare %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr, i64) declare %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr, i64)
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64) declare %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String")
declare ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String") declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
declare { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface", ptr) declare { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface", ptr)
declare { float, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Float32"(%"github.com/goplus/llgo/internal/runtime.iface", ptr) declare { %"github.com/goplus/llgo/internal/runtime.String", i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2String"(%"github.com/goplus/llgo/internal/runtime.iface", ptr)
declare { double, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Float64"(%"github.com/goplus/llgo/internal/runtime.iface", ptr) declare ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice")

View File

@@ -13,7 +13,7 @@ _llgo_0:
br i1 %1, label %_llgo_1, label %_llgo_2 br i1 %1, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0 _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) %3 = call i32 (ptr, ...) @printf(ptr @0, i32 %0, ptr %2)
call void @exit(i32 1) call void @exit(i32 1)
br label %_llgo_2 br label %_llgo_2
@@ -45,11 +45,11 @@ _llgo_0:
%3 = extractvalue { ptr, i32 } %2, 0 %3 = extractvalue { ptr, i32 } %2, 0
%4 = extractvalue { ptr, i32 } %2, 1 %4 = extractvalue { ptr, i32 } %2, 1
call void @main.check(i32 %4) call void @main.check(i32 %4)
%5 = call i32 @sqlite3_close() %5 = call i32 @sqlite3_close(ptr %3)
ret void ret void
} }
declare ptr @sqlite3_errstr() declare ptr @sqlite3_errstr(i32)
declare i32 @printf(ptr, ...) 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 { ptr, i32 } @"github.com/goplus/llgo/x/sqlite.OpenV2"(ptr, i32, ptr)
declare i32 @sqlite3_close() declare i32 @sqlite3_close(ptr)

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

@@ -0,0 +1,15 @@
package main
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/py"
"github.com/goplus/llgo/py/math"
"github.com/goplus/llgo/py/os"
)
func main() {
x := math.Sqrt(py.Float(2))
wd := os.Getcwd()
c.Printf(c.Str("sqrt(2) = %f\n"), x.Float64())
c.Printf(c.Str("cwd = %s\n"), wd.CStr())
}

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

@@ -0,0 +1,74 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@__llgo_argc = global ptr null
@__llgo_argv = global ptr null
@__llgo_py.math.sqrt = linkonce global ptr null
@__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:
%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
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
ret void
}
define void @main(i32 %0, ptr %1) {
_llgo_0:
call void @Py_Initialize()
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 @PyFloat_FromDouble(double 2.000000e+00)
%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
}
declare void @"github.com/goplus/llgo/py/math.init"()
declare void @"github.com/goplus/llgo/py/os.init"()
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @PyFloat_FromDouble(double)
declare ptr @PyObject_CallOneArg(ptr, ptr)
declare ptr @PyObject_CallNoArgs(ptr)
declare double @PyFloat_AsDouble(ptr)
declare i32 @printf(ptr, ...)
declare ptr @PyBytes_AsString(ptr)
declare void @llgoLoadPyModSyms(ptr, ...)
declare void @Py_Initialize()

14
cl/_testpy/math/in.go Normal file
View File

@@ -0,0 +1,14 @@
package math
import (
_ "unsafe"
"github.com/goplus/llgo/py"
)
const (
LLGoPackage = "py.math"
)
//go:linkname Sqrt py.sqrt
func Sqrt(x *py.Object) *py.Object

29
cl/_testpy/math/out.ll Normal file
View File

@@ -0,0 +1,29 @@
; ModuleID = 'math'
source_filename = "math"
@__llgo_py.math.sqrt = external global ptr
@"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
define void @math.init() {
_llgo_0:
%0 = load i1, ptr @"math.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_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
_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
br label %_llgo_2
}
declare ptr @PyImport_ImportModule(ptr)

View File

@@ -34,7 +34,7 @@ _llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"() call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init() call void @main.init()
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @main.hello() %2 = call %"github.com/goplus/llgo/internal/runtime.String" @main.hello()
%3 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %2) %3 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %2, 1
%4 = add i64 %3, 1 %4 = add i64 %3, 1
%5 = alloca i8, i64 %4, align 1 %5 = alloca i8, i64 %4, align 1
%6 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %5, %"github.com/goplus/llgo/internal/runtime.String" %2) %6 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %5, %"github.com/goplus/llgo/internal/runtime.String" %2)
@@ -46,8 +46,6 @@ declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/ll
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String") declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String")
declare i32 @printf(ptr, ...) declare i32 @printf(ptr, ...)

View File

@@ -131,14 +131,14 @@ _llgo_0:
%60 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 5) %60 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 5)
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %60) call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %60)
%61 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 5) %61 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 5)
%62 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %61) %62 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %61, 1
%63 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %61, i64 1, i64 %62) %63 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %61, i64 1, i64 %62)
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %63) call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %63)
%64 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 5) %64 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 5)
%65 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %64, i64 1, i64 2) %65 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %64, i64 1, i64 2)
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %65) call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %65)
%66 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 5) %66 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 5)
%67 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %66) %67 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %66, 1
%68 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %66, i64 5, i64 %67) %68 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %66, i64 5, i64 %67)
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %68) call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %68)
ret void ret void
@@ -152,7 +152,7 @@ _llgo_0:
define void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %0) { define void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0: _llgo_0:
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %0) %1 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %0, 1
call void @main.out(i64 %1) call void @main.out(i64 %1)
ret void ret void
} }
@@ -171,8 +171,6 @@ declare ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/go
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64) declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String", i64, i64) declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String", i64, i64)
declare i32 @printf(ptr, ...) declare i32 @printf(ptr, ...)

View File

@@ -81,7 +81,7 @@ _llgo_0:
%9 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %2, i64 16, i64 3, i64 0, i64 3, i64 3) %9 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %2, i64 16, i64 3, i64 0, i64 3, i64 3)
%10 = call %"github.com/goplus/llgo/internal/runtime.String" @main.concat(%"github.com/goplus/llgo/internal/runtime.Slice" %9) %10 = call %"github.com/goplus/llgo/internal/runtime.String" @main.concat(%"github.com/goplus/llgo/internal/runtime.Slice" %9)
%11 = load ptr, ptr @__stderrp, align 8 %11 = load ptr, ptr @__stderrp, align 8
%12 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %10) %12 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %10, 1
%13 = add i64 %12, 1 %13 = add i64 %12, 1
%14 = alloca i8, i64 %13, align 1 %14 = alloca i8, i64 %13, align 1
%15 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %14, %"github.com/goplus/llgo/internal/runtime.String" %10) %15 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %14, %"github.com/goplus/llgo/internal/runtime.String" %10)
@@ -103,8 +103,6 @@ declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64) declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String") declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String")
declare i32 @fprintf(ptr, ptr, ...) declare i32 @fprintf(ptr, ptr, ...)

View File

@@ -119,12 +119,12 @@ _llgo_0:
%61 = load i64, ptr %60, align 4 %61 = load i64, ptr %60, align 4
%62 = call i32 (ptr, ...) @printf(ptr @3, i64 %61) %62 = call i32 (ptr, ...) @printf(ptr @3, i64 %61)
%63 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @5, i64 6) %63 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @5, i64 6)
%64 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %63) %64 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %63, 0
%65 = getelementptr inbounds i8, ptr %64, i64 2 %65 = getelementptr inbounds i8, ptr %64, i64 2
%66 = load i8, ptr %65, align 1 %66 = load i8, ptr %65, align 1
%67 = call i32 (ptr, ...) @printf(ptr @4, i8 %66) %67 = call i32 (ptr, ...) @printf(ptr @4, i8 %66)
%68 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @7, i64 6) %68 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @7, i64 6)
%69 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %68) %69 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %68, 0
%70 = getelementptr inbounds i8, ptr %69, i64 1 %70 = getelementptr inbounds i8, ptr %69, i64 1
%71 = load i8, ptr %70, align 1 %71 = load i8, ptr %70, align 1
%72 = call i32 (ptr, ...) @printf(ptr @6, i8 %71) %72 = call i32 (ptr, ...) @printf(ptr @6, i8 %71)
@@ -138,5 +138,3 @@ declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64)
declare i32 @printf(ptr, ...) declare i32 @printf(ptr, ...)
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64) declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
declare ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String")

39
cl/_testrt/named/in.go Normal file
View File

@@ -0,0 +1,39 @@
package main
import "github.com/goplus/llgo/internal/runtime/c"
type mSpanList struct {
first *mspan
last *mspan
}
type minfo struct {
span *mspan
info int
}
type mspan struct {
next *mspan
prev *mspan
list *mSpanList
info minfo
value int
check func(int) int
}
func main() {
m := &mspan{}
m.value = 100
m.next = &mspan{}
m.next.value = 200
m.list = &mSpanList{}
m.list.last = &mspan{}
m.list.last.value = 300
m.info.info = 10
m.info.span = m
m.check = func(n int) int {
return m.value * n
}
c.Printf(c.Str("%d %d %d %d %d %d\n"), m.next.value, m.list.last.value, m.info.info,
m.info.span.value, m.check(-2), m.info.span.check(-3))
}

143
cl/_testrt/named/out.ll Normal file
View File

@@ -0,0 +1,143 @@
; ModuleID = 'main'
source_filename = "main"
%main.mspan = type { ptr, ptr, ptr, %main.minfo, i64, { ptr, ptr } }
%main.minfo = type { ptr, i64 }
%main.mSpanList = type { ptr, ptr }
@"main.init$guard" = global ptr null
@__llgo_argc = global ptr null
@__llgo_argv = global ptr null
@0 = private unnamed_addr constant [19 x i8] c"%d %d %d %d %d %d\0A\00", align 1
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 @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8)
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 64)
store ptr %3, ptr %2, align 8
%4 = load ptr, ptr %2, align 8
%5 = getelementptr inbounds %main.mspan, ptr %4, i32 0, i32 4
store i64 100, ptr %5, align 4
%6 = load ptr, ptr %2, align 8
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 64)
%8 = getelementptr inbounds %main.mspan, ptr %6, i32 0, i32 0
store ptr %7, ptr %8, align 8
%9 = load ptr, ptr %2, align 8
%10 = getelementptr inbounds %main.mspan, ptr %9, i32 0, i32 0
%11 = load ptr, ptr %10, align 8
%12 = getelementptr inbounds %main.mspan, ptr %11, i32 0, i32 4
store i64 200, ptr %12, align 4
%13 = load ptr, ptr %2, align 8
%14 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 16)
%15 = getelementptr inbounds %main.mspan, ptr %13, i32 0, i32 2
store ptr %14, ptr %15, align 8
%16 = load ptr, ptr %2, align 8
%17 = getelementptr inbounds %main.mspan, ptr %16, i32 0, i32 2
%18 = load ptr, ptr %17, align 8
%19 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 64)
%20 = getelementptr inbounds %main.mSpanList, ptr %18, i32 0, i32 1
store ptr %19, ptr %20, align 8
%21 = load ptr, ptr %2, align 8
%22 = getelementptr inbounds %main.mspan, ptr %21, i32 0, i32 2
%23 = load ptr, ptr %22, align 8
%24 = getelementptr inbounds %main.mSpanList, ptr %23, i32 0, i32 1
%25 = load ptr, ptr %24, align 8
%26 = getelementptr inbounds %main.mspan, ptr %25, i32 0, i32 4
store i64 300, ptr %26, align 4
%27 = load ptr, ptr %2, align 8
%28 = getelementptr inbounds %main.mspan, ptr %27, i32 0, i32 3
%29 = getelementptr inbounds %main.minfo, ptr %28, i32 0, i32 1
store i64 10, ptr %29, align 4
%30 = load ptr, ptr %2, align 8
%31 = getelementptr inbounds %main.mspan, ptr %30, i32 0, i32 3
%32 = load ptr, ptr %2, align 8
%33 = getelementptr inbounds %main.minfo, ptr %31, i32 0, i32 0
store ptr %32, ptr %33, align 8
%34 = load ptr, ptr %2, align 8
%35 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8)
%36 = getelementptr inbounds { ptr }, ptr %35, i32 0, i32 0
store ptr %2, ptr %36, align 8
%37 = alloca { ptr, ptr }, align 8
%38 = getelementptr inbounds { ptr, ptr }, ptr %37, i32 0, i32 0
store ptr @"main.main$1", ptr %38, align 8
%39 = getelementptr inbounds { ptr, ptr }, ptr %37, i32 0, i32 1
store ptr %35, ptr %39, align 8
%40 = load { ptr, ptr }, ptr %37, align 8
%41 = getelementptr inbounds %main.mspan, ptr %34, i32 0, i32 5
store { ptr, ptr } %40, ptr %41, align 8
%42 = load ptr, ptr %2, align 8
%43 = getelementptr inbounds %main.mspan, ptr %42, i32 0, i32 0
%44 = load ptr, ptr %43, align 8
%45 = getelementptr inbounds %main.mspan, ptr %44, i32 0, i32 4
%46 = load i64, ptr %45, align 4
%47 = load ptr, ptr %2, align 8
%48 = getelementptr inbounds %main.mspan, ptr %47, i32 0, i32 2
%49 = load ptr, ptr %48, align 8
%50 = getelementptr inbounds %main.mSpanList, ptr %49, i32 0, i32 1
%51 = load ptr, ptr %50, align 8
%52 = getelementptr inbounds %main.mspan, ptr %51, i32 0, i32 4
%53 = load i64, ptr %52, align 4
%54 = load ptr, ptr %2, align 8
%55 = getelementptr inbounds %main.mspan, ptr %54, i32 0, i32 3
%56 = getelementptr inbounds %main.minfo, ptr %55, i32 0, i32 1
%57 = load i64, ptr %56, align 4
%58 = load ptr, ptr %2, align 8
%59 = getelementptr inbounds %main.mspan, ptr %58, i32 0, i32 3
%60 = getelementptr inbounds %main.minfo, ptr %59, i32 0, i32 0
%61 = load ptr, ptr %60, align 8
%62 = getelementptr inbounds %main.mspan, ptr %61, i32 0, i32 4
%63 = load i64, ptr %62, align 4
%64 = load ptr, ptr %2, align 8
%65 = getelementptr inbounds %main.mspan, ptr %64, i32 0, i32 5
%66 = load { ptr, ptr }, ptr %65, align 8
%67 = extractvalue { ptr, ptr } %66, 1
%68 = extractvalue { ptr, ptr } %66, 0
%69 = call i64 %68(ptr %67, i64 -2)
%70 = load ptr, ptr %2, align 8
%71 = getelementptr inbounds %main.mspan, ptr %70, i32 0, i32 3
%72 = getelementptr inbounds %main.minfo, ptr %71, i32 0, i32 0
%73 = load ptr, ptr %72, align 8
%74 = getelementptr inbounds %main.mspan, ptr %73, i32 0, i32 5
%75 = load { ptr, ptr }, ptr %74, align 8
%76 = extractvalue { ptr, ptr } %75, 1
%77 = extractvalue { ptr, ptr } %75, 0
%78 = call i64 %77(ptr %76, i64 -3)
%79 = call i32 (ptr, ...) @printf(ptr @0, i64 %46, i64 %53, i64 %57, i64 %63, i64 %69, i64 %78)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
define i64 @"main.main$1"(ptr %0, i64 %1) {
_llgo_0:
%2 = load { ptr }, ptr %0, align 8
%3 = extractvalue { ptr } %2, 0
%4 = load ptr, ptr %3, align 8
%5 = getelementptr inbounds %main.mspan, ptr %4, i32 0, i32 4
%6 = load i64, ptr %5, align 4
%7 = mul i64 %6, %1
ret i64 %7
}
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64)
declare i32 @printf(ptr, ...)

View File

@@ -147,11 +147,23 @@ func TestCompileEx(t *testing.T, src any, fname, expected string) {
} }
return rt return rt
}) })
prog.SetPython(func() *types.Package {
rt, err := imp.Import(llssa.PkgPython)
if err != nil {
t.Fatal("load python failed:", err)
}
return rt
})
ret, err := cl.NewPackage(prog, foo, files) ret, err := cl.NewPackage(prog, foo, files)
if err != nil { if err != nil {
t.Fatal("cl.NewPackage failed:", err) t.Fatal("cl.NewPackage failed:", err)
} }
if prog.NeedPyInit { // call PyInit if needed
ret.PyInit()
}
if v := ret.String(); v != expected { if v := ret.String(); v != expected {
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected) t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
} }

View File

@@ -123,6 +123,7 @@ type instrOrValue interface {
const ( const (
PkgNormal = iota PkgNormal = iota
PkgLLGo PkgLLGo
PkgPyModule // py.<module>
PkgNoInit // noinit: a package that don't need to be initialized PkgNoInit // noinit: a package that don't need to be initialized
PkgDeclOnly // decl: a package that only have declarations PkgDeclOnly // decl: a package that only have declarations
PkgLinkIR // link llvm ir (.ll) PkgLinkIR // link llvm ir (.ll)
@@ -142,6 +143,7 @@ type context struct {
goProg *ssa.Program goProg *ssa.Program
goTyps *types.Package goTyps *types.Package
goPkg *ssa.Package goPkg *ssa.Package
pyMod string
link map[string]string // pkgPath.nameInPkg => linkname link map[string]string // pkgPath.nameInPkg => linkname
loaded map[*types.Package]*pkgInfo // loaded packages loaded map[*types.Package]*pkgInfo // loaded packages
bvals map[ssa.Value]llssa.Expr // block values bvals map[ssa.Value]llssa.Expr // block values
@@ -174,7 +176,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
for i, n := 0, mthds.Len(); i < n; i++ { for i, n := 0, mthds.Len(); i < n; i++ {
mthd := mthds.At(i) mthd := mthds.At(i)
if ssaMthd := prog.MethodValue(mthd); ssaMthd != nil { if ssaMthd := prog.MethodValue(mthd); ssaMthd != nil {
p.compileFuncDecl(pkg, ssaMthd) p.compileFuncDecl(pkg, ssaMthd, false)
} }
} }
} }
@@ -209,14 +211,19 @@ var (
argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8])) argvTy = types.NewPointer(types.NewPointer(types.Typ[types.Int8]))
) )
func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) llssa.Function { func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function, call bool) (llssa.Function, llssa.PyObjRef, int) {
pkgTypes, name, ftype := p.funcName(f, true) pkgTypes, name, ftype := p.funcName(f, true)
if ftype != goFunc { if ftype != goFunc {
return nil if ftype == pyFunc {
// TODO(xsw): pyMod == ""
fnName := pysymPrefix + p.pyMod + "." + name
return nil, pkg.NewPyFunc(fnName, f.Signature, call), pyFunc
}
return nil, nil, ignoredFunc
} }
fn := pkg.FuncOf(name) fn := pkg.FuncOf(name)
if fn != nil && fn.HasBody() { if fn != nil && fn.HasBody() {
return fn return fn, nil, goFunc
} }
var sig = f.Signature var sig = f.Signature
@@ -262,21 +269,34 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) llssa.Func
off[i] = p.compilePhis(b, block) off[i] = p.compilePhis(b, block)
} }
for i, block := range f.Blocks { for i, block := range f.Blocks {
p.compileBlock(b, block, off[i], i == 0 && name == "main") 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 { for _, phi := range p.phis {
phi() phi()
} }
}) })
} }
return fn return fn, nil, goFunc
} }
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc. // funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
// or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc. // or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc.
func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) { func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObjRef, ftype int) {
_, name, ftype := p.funcName(fn, false) pkgTypes, name, ftype := p.funcName(fn, false)
if ftype == llgoInstr { switch ftype {
case pyFunc:
if kind, mod := pkgKindByScope(pkgTypes.Scope()); kind == PkgPyModule {
pkg := p.pkg
fnName := pysymPrefix + mod + "." + name
if pyFn = pkg.PyObjOf(fnName); pyFn == nil {
pyFn = pkg.NewPyFunc(fnName, fn.Signature, true)
return
}
}
ftype = ignoredFunc
case llgoInstr:
switch name { switch name {
case "cstr": case "cstr":
ftype = llgoCstr ftype = llgoCstr
@@ -288,31 +308,71 @@ func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
ftype = llgoAlloca ftype = llgoAlloca
case "allocaCStr": case "allocaCStr":
ftype = llgoAllocaCStr ftype = llgoAllocaCStr
case "stringData":
ftype = llgoStringData
case "unreachable": case "unreachable":
ftype = llgoUnreachable ftype = llgoUnreachable
case "bitCastTo64F":
ftype = llgoBitCastTo64F
case "bitCastTo32F":
ftype = llgoBitCastTo32F
default: default:
panic("unknown llgo instruction: " + name) panic("unknown llgo instruction: " + name)
} }
} else { default:
pkg := p.pkg pkg := p.pkg
if ret = pkg.FuncOf(name); ret == nil && len(fn.FreeVars) == 0 { if aFn = pkg.FuncOf(name); aFn == nil {
if len(fn.FreeVars) > 0 {
return nil, nil, ignoredFunc
}
sig := fn.Signature sig := fn.Signature
ret = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false) aFn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false)
} }
} }
return return
} }
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, doInit bool) llssa.BasicBlock { func modOf(name string) string {
ret := p.fn.Block(block.Index) 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 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) b.SetBlock(ret)
if doInit { if doModInit {
prog := p.prog if pyModInit = p.pyMod != ""; pyModInit {
pkg := p.pkg last = len(instrs) - 1
instrs = instrs[:last]
} else {
// TODO(xsw): confirm pyMod don't need to call LoadPyModSyms
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)
}
// sort by module name
modNames := make([]string, 0, len(mods))
for modName := range mods {
modNames = append(modNames, modName)
}
sort.Strings(modNames)
b.SetBlockEx(ret, llssa.AfterInit)
for _, modName := range modNames {
objs := mods[modName]
b.LoadPyModSyms(modName, objs...)
}
}
})
}
} else if doMainInit {
fn := p.fn fn := p.fn
argc := pkg.NewVar("__llgo_argc", types.NewPointer(types.Typ[types.Int32]), llssa.InC) argc := pkg.NewVar("__llgo_argc", types.NewPointer(types.Typ[types.Int32]), llssa.InC)
argv := pkg.NewVar("__llgo_argv", types.NewPointer(argvTy), llssa.InC) argv := pkg.NewVar("__llgo_argv", types.NewPointer(argvTy), llssa.InC)
@@ -323,9 +383,23 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
callRuntimeInit(b, pkg) callRuntimeInit(b, pkg)
b.Call(pkg.FuncOf("main.init").Expr) b.Call(pkg.FuncOf("main.init").Expr)
} }
for _, instr := range block.Instrs[n:] { for _, instr := range instrs {
p.compileInstr(b, instr) p.compileInstr(b, instr)
} }
if pyModInit {
jump := block.Instrs[n+last].(*ssa.Jump)
jumpTo := p.jumpTo(jump)
modPath := p.pyMod
modName := pysymPrefix + modPath
modPtr := pkg.NewPyModVar(modName, true).Expr
mod := b.Load(modPtr)
cond := b.BinOp(token.NEQ, mod, prog.Null(mod.Type))
newBlk := p.fn.MakeBlock()
b.If(cond, jumpTo, newBlk)
b.SetBlock(newBlk)
b.Store(modPtr, b.ImportPyMod(modPath))
b.Jump(jumpTo)
}
return ret return ret
} }
@@ -334,8 +408,7 @@ const (
) )
func callRuntimeInit(b llssa.Builder, pkg llssa.Package) { func callRuntimeInit(b llssa.Builder, pkg llssa.Package) {
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) fn := pkg.NewFunc(RuntimeInit, llssa.NoArgsNoRet, llssa.InC) // don't need to convert runtime.init
fn := pkg.NewFunc(RuntimeInit, sig, llssa.InC) // don't need to convert runtime.init
b.Call(fn.Expr) b.Call(fn.Expr)
} }
@@ -434,6 +507,15 @@ func (p *context) allocaCStr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr)
panic("allocaCStr(s string): invalid arguments") panic("allocaCStr(s string): invalid arguments")
} }
// func stringData(s string) *int8
func (p *context) stringData(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 1 {
s := p.compileValue(b, args[0])
return b.StringData(s)
}
panic("stringData(s string): invalid arguments")
}
func isPhi(i ssa.Instruction) bool { func isPhi(i ssa.Instruction) bool {
_, ok := i.(*ssa.Phi) _, ok := i.(*ssa.Phi)
return ok return ok
@@ -441,7 +523,7 @@ func isPhi(i ssa.Instruction) bool {
func (p *context) compilePhis(b llssa.Builder, block *ssa.BasicBlock) int { func (p *context) compilePhis(b llssa.Builder, block *ssa.BasicBlock) int {
ret := p.fn.Block(block.Index) ret := p.fn.Block(block.Index)
b.SetBlock(ret) b.SetBlockEx(ret, llssa.AtEnd)
if ninstr := len(block.Instrs); ninstr > 0 { if ninstr := len(block.Instrs); ninstr > 0 {
if isPhi(block.Instrs[0]) { if isPhi(block.Instrs[0]) {
n := 1 n := 1
@@ -510,11 +592,14 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
ret = b.BuiltinCall(fn, args...) ret = b.BuiltinCall(fn, args...)
} }
case *ssa.Function: case *ssa.Function:
fn, ftype := p.compileFunction(cv) aFn, pyFn, ftype := p.compileFunction(cv)
switch ftype { switch ftype {
case goFunc, cFunc: case goFunc, cFunc:
args := p.compileValues(b, args, kind) args := p.compileValues(b, args, kind)
ret = b.Call(fn.Expr, args...) ret = b.Call(aFn.Expr, args...)
case pyFunc:
args := p.compileValues(b, args, kind)
ret = b.Call(pyFn.Expr, args...)
case llgoCstr: case llgoCstr:
ret = cstr(b, args) ret = cstr(b, args)
case llgoAdvance: case llgoAdvance:
@@ -525,12 +610,10 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
ret = p.alloca(b, args) ret = p.alloca(b, args)
case llgoAllocaCStr: case llgoAllocaCStr:
ret = p.allocaCStr(b, args) ret = p.allocaCStr(b, args)
case llgoStringData:
ret = p.stringData(b, args)
case llgoUnreachable: // func unreachable() case llgoUnreachable: // func unreachable()
b.Unreachable() b.Unreachable()
case llgoBitCastTo32F:
ret = b.BitCast(p.compileValue(b, args[0]), b.Prog.Type(types.Typ[types.Float32], llssa.InGo))
case llgoBitCastTo64F:
ret = b.BitCast(p.compileValue(b, args[0]), b.Prog.Float64())
default: default:
panic("todo") panic("todo")
} }
@@ -650,6 +733,12 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
return ret return ret
} }
func (p *context) jumpTo(v *ssa.Jump) llssa.BasicBlock {
fn := p.fn
succs := v.Block().Succs
return fn.Block(succs[0].Index)
}
func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
if iv, ok := instr.(instrOrValue); ok { if iv, ok := instr.(instrOrValue); ok {
p.compileInstrOrValue(b, iv, false) p.compileInstrOrValue(b, iv, false)
@@ -673,9 +762,7 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
val := p.compileValue(b, v.Val) val := p.compileValue(b, v.Val)
b.Store(ptr, val) b.Store(ptr, val)
case *ssa.Jump: case *ssa.Jump:
fn := p.fn jmpb := p.jumpTo(v)
succs := v.Block().Succs
jmpb := fn.Block(succs[0].Index)
b.Jump(jmpb) b.Jump(jmpb)
case *ssa.Return: case *ssa.Return:
var results []llssa.Expr var results []llssa.Expr
@@ -706,12 +793,13 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
} }
} }
func (p *context) compileFunction(v *ssa.Function) (llssa.Function, int) { func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyObjRef, kind int) {
// v.Pkg == nil: means auto generated function? // v.Pkg == nil: means auto generated function?
if v.Pkg == p.goPkg || v.Pkg == nil { if v.Pkg == p.goPkg || v.Pkg == nil {
// function in this package // function in this package
if fn := p.compileFuncDecl(p.pkg, v); fn != nil { goFn, pyFn, kind = p.compileFuncDecl(p.pkg, v, true)
return fn, goFunc if kind != ignoredFunc {
return
} }
} }
return p.funcOf(v) return p.funcOf(v)
@@ -730,8 +818,11 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
} }
} }
case *ssa.Function: case *ssa.Function:
fn, _ := p.compileFunction(v) aFn, pyFn, _ := p.compileFunction(v)
return fn.Expr if aFn != nil {
return aFn.Expr
}
return pyFn.Expr
case *ssa.Global: case *ssa.Global:
g := p.varOf(v) g := p.varOf(v)
return g.Expr return g.Expr
@@ -814,6 +905,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
types.Unsafe: {kind: PkgDeclOnly}, // TODO(xsw): PkgNoInit or PkgDeclOnly? types.Unsafe: {kind: PkgDeclOnly}, // TODO(xsw): PkgNoInit or PkgDeclOnly?
}, },
} }
ctx.initPyModule()
ctx.initFiles(pkgPath, files) ctx.initFiles(pkgPath, files)
for _, m := range members { for _, m := range members {
member := m.val member := m.val
@@ -824,7 +916,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
// Do not try to build generic (non-instantiated) functions. // Do not try to build generic (non-instantiated) functions.
continue continue
} }
ctx.compileFuncDecl(ret, member) ctx.compileFuncDecl(ret, member, false)
case *ssa.Type: case *ssa.Type:
ctx.compileType(ret, member) ctx.compileType(ret, member)
case *ssa.Global: case *ssa.Global:

View File

@@ -28,8 +28,12 @@ func testCompile(t *testing.T, src, expected string) {
cltest.TestCompileEx(t, src, "foo.go", expected) cltest.TestCompileEx(t, src, "foo.go", expected)
} }
func TestFromTestpy(t *testing.T) {
cltest.FromDir(t, "", "./_testpy", false)
}
func TestFromTestlibc(t *testing.T) { func TestFromTestlibc(t *testing.T) {
cltest.FromDir(t, "", "./_testlibc", false) cltest.FromDir(t, "", "./_testlibc", true)
} }
func TestFromTestrt(t *testing.T) { func TestFromTestrt(t *testing.T) {
@@ -44,6 +48,14 @@ func TestSqlite(t *testing.T) {
cltest.Pkg(t, "github.com/goplus/llgo/x/sqlite", "../x/sqlite/sqlite.ll") cltest.Pkg(t, "github.com/goplus/llgo/x/sqlite", "../x/sqlite/sqlite.ll")
} }
func TestFromTestpymath(t *testing.T) {
cltest.Pkg(t, ssa.PkgPython+"/math", "../py/math/llgo_autogen.ll")
}
func TestPython(t *testing.T) {
cltest.Pkg(t, ssa.PkgPython, "../py/llgo_autogen.ll")
}
func TestRuntime(t *testing.T) { func TestRuntime(t *testing.T) {
cltest.Pkg(t, ssa.PkgRuntime, "../internal/runtime/llgo_autogen.ll") cltest.Pkg(t, ssa.PkgRuntime, "../internal/runtime/llgo_autogen.ll")
} }

View File

@@ -30,6 +30,8 @@ import (
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
) )
// -----------------------------------------------------------------------------
type symInfo struct { type symInfo struct {
file string file string
fullName string fullName string
@@ -101,6 +103,8 @@ func pkgKind(v string) (int, string) {
// return PkgLinkBitCode // return PkgLinkBitCode
if strings.HasPrefix(v, "link:") { // "link: <libpath>" if strings.HasPrefix(v, "link:") { // "link: <libpath>"
return PkgLinkExtern, v[5:] return PkgLinkExtern, v[5:]
} else if strings.HasPrefix(v, "py.") { // "py.<module>"
return PkgPyModule, v[3:]
} }
} }
return PkgLLGo, "" return PkgLLGo, ""
@@ -210,7 +214,7 @@ func (p *context) initLink(line string, prefix int, f func(inPkgName string) (fu
} else { } else {
panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf") panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf")
} }
} else { } else if c := inPkgName[0]; c >= 'A' && c <= 'Z' {
fmt.Fprintln(os.Stderr, "==>", line) fmt.Fprintln(os.Stderr, "==>", line)
fmt.Fprintf(os.Stderr, "llgo: linkname %s not found and ignored\n", inPkgName) fmt.Fprintf(os.Stderr, "llgo: linkname %s not found and ignored\n", inPkgName)
} }
@@ -303,17 +307,17 @@ const (
ignoredFunc = iota ignoredFunc = iota
goFunc = int(llssa.InGo) goFunc = int(llssa.InGo)
cFunc = int(llssa.InC) cFunc = int(llssa.InC)
pyFunc = int(llssa.InPython)
llgoInstr = -1 llgoInstr = -1
llgoInstrBase = 0x80 llgoInstrBase = 0x80
llgoUnreachable = llgoInstrBase + 0 llgoUnreachable = llgoInstrBase + 0
llgoCstr = llgoInstrBase + 1 llgoCstr = llgoInstrBase + 1
llgoAlloca = llgoInstrBase + 2 llgoAlloca = llgoInstrBase + 2
llgoAllocaCStr = llgoInstrBase + 3 llgoAllocaCStr = llgoInstrBase + 3
llgoAdvance = llgoInstrBase + 4 llgoAdvance = llgoInstrBase + 4
llgoIndex = llgoInstrBase + 5 llgoIndex = llgoInstrBase + 5
llgoBitCastTo32F = llgoInstrBase + 6 llgoStringData = llgoInstrBase + 6
llgoBitCastTo64F = llgoInstrBase + 7
) )
func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, string, int) { func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, string, int) {
@@ -339,6 +343,9 @@ func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, strin
if strings.HasPrefix(v, "C.") { if strings.HasPrefix(v, "C.") {
return nil, v[2:], cFunc return nil, v[2:], cFunc
} }
if strings.HasPrefix(v, "py.") {
return pkg, v[3:], pyFunc
}
if strings.HasPrefix(v, "llgo.") { if strings.HasPrefix(v, "llgo.") {
return nil, v[5:], llgoInstr return nil, v[5:], llgoInstr
} }
@@ -391,3 +398,17 @@ func pkgKindByPath(pkgPath string) int {
} }
return PkgNormal return PkgNormal
} }
// -----------------------------------------------------------------------------
const (
pysymPrefix = "__llgo_py."
)
func (p *context) initPyModule() {
if kind, mod := pkgKindByScope(p.goTyps.Scope()); kind == PkgPyModule {
p.pyMod = mod
}
}
// -----------------------------------------------------------------------------

2
go.mod
View File

@@ -18,3 +18,5 @@ require (
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.7.0 // indirect
) )
retract v0.8.0

View File

@@ -29,7 +29,7 @@ import (
"time" "time"
wasm "github.com/aykevl/go-wasm" wasm "github.com/aykevl/go-wasm"
"github.com/goplus/llgo/x/ar" "github.com/goplus/llgo/xtool/ar"
) )
// Create creates an arcive for static linking from a list of object files // Create creates an arcive for static linking from a list of object files

View File

@@ -33,7 +33,7 @@ import (
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"github.com/goplus/llgo/cl" "github.com/goplus/llgo/cl"
"github.com/goplus/llgo/x/clang" "github.com/goplus/llgo/xtool/clang"
llssa "github.com/goplus/llgo/ssa" llssa "github.com/goplus/llgo/ssa"
) )
@@ -120,18 +120,31 @@ func Do(args []string, conf *Config) {
cl.SetDebug(cl.DbgFlagAll) cl.SetDebug(cl.DbgFlagAll)
} }
var needRt bool
var rt []*packages.Package var rt []*packages.Package
prog := llssa.NewProgram(nil) prog := llssa.NewProgram(nil)
load := func() []*packages.Package {
if rt == nil {
var err error
rt, err = packages.Load(cfg, llssa.PkgRuntime, llssa.PkgPython)
check(err)
}
return rt
}
prog.SetRuntime(func() *types.Package { prog.SetRuntime(func() *types.Package {
rt, err = packages.Load(cfg, llssa.PkgRuntime) needRt = true
check(err) rt := load()
return rt[0].Types return rt[0].Types
}) })
prog.SetPython(func() *types.Package {
rt := load()
return rt[1].Types
})
pkgs := buildAllPkgs(prog, initial, mode, verbose) pkgs := buildAllPkgs(prog, initial, mode, verbose)
var runtimeFiles []string var runtimeFiles []string
if rt != nil { if needRt {
runtimeFiles = allLinkFiles(rt) runtimeFiles = allLinkFiles(rt)
} }
if mode != ModeBuild { if mode != ModeBuild {
@@ -147,12 +160,22 @@ func Do(args []string, conf *Config) {
} }
} }
func setNeedRuntime(pkg *packages.Package) { func setNeedRuntimeOrPyInit(pkg *packages.Package, needRuntime, needPyInit bool) {
pkg.ID = "" // just use pkg.Module to mark it needs runtime v := []byte{'0', '0'}
if needRuntime {
v[0] = '1'
}
if needPyInit {
v[1] = '1'
}
pkg.ID = string(v) // just use pkg.ID to mark it needs runtime
} }
func isNeedRuntime(pkg *packages.Package) bool { func isNeedRuntimeOrPyInit(pkg *packages.Package) (needRuntime, needPyInit bool) {
return pkg.ID == "" if len(pkg.ID) == 2 {
return pkg.ID[0] == '1', pkg.ID[1] == '1'
}
return
} }
func buildAllPkgs(prog llssa.Program, initial []*packages.Package, mode Mode, verbose bool) (pkgs []*aPackage) { func buildAllPkgs(prog llssa.Program, initial []*packages.Package, mode Mode, verbose bool) (pkgs []*aPackage) {
@@ -172,28 +195,29 @@ func buildAllPkgs(prog llssa.Program, initial []*packages.Package, mode Mode, ve
// skip packages that only contain declarations // skip packages that only contain declarations
// and set no export file // and set no export file
pkg.ExportFile = "" pkg.ExportFile = ""
case cl.PkgLinkIR: case cl.PkgLinkIR, cl.PkgLinkExtern, cl.PkgPyModule:
// skip packages that don't need to be compiled but need to be linked
pkgPath := pkg.PkgPath pkgPath := pkg.PkgPath
if isPkgInLLGo(pkgPath) { if isPkgInLLGo(pkgPath) {
pkg.ExportFile = concatPkgLinkFiles(pkgPath) pkg.ExportFile = concatPkgLinkFiles(pkgPath)
} else { } else {
panic("todo") panic("todo")
} }
case cl.PkgLinkExtern: if kind == cl.PkgLinkExtern { // need to be linked with external library
// skip packages that don't need to be compiled but need to be linked with external library linkFile := os.ExpandEnv(strings.TrimSpace(param))
linkFile := os.ExpandEnv(strings.TrimSpace(param)) dir, lib := filepath.Split(linkFile)
dir, lib := filepath.Split(linkFile) command := " -l " + lib
command := " -l " + lib if dir != "" {
if dir != "" { command += " -L " + dir
command += " -L " + dir }
if isMultiLinkFiles(pkg.ExportFile) {
pkg.ExportFile = command + pkg.ExportFile
} else {
pkg.ExportFile = command + " " + pkg.ExportFile
}
} }
pkg.ExportFile = command
default: default:
buildPkg(prog, aPkg, mode, verbose) buildPkg(prog, aPkg, mode, verbose)
if prog.NeedRuntime() { setNeedRuntimeOrPyInit(pkg, prog.NeedRuntime, prog.NeedPyInit)
setNeedRuntime(pkg)
}
} }
} }
return return
@@ -212,27 +236,43 @@ func linkMainPkg(pkg *packages.Package, pkgs []*aPackage, runtimeFiles []string,
args[1] = app args[1] = app
args[2] = "-Wno-override-module" args[2] = "-Wno-override-module"
needRuntime := false needRuntime := false
needPyInit := false
packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) { packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) {
if p.ExportFile != "" && !isRuntimePkg(p.PkgPath) { // skip packages that only contain declarations if p.ExportFile != "" && !isRuntimePkg(p.PkgPath) { // skip packages that only contain declarations
args = appendLinkFiles(args, p.ExportFile) args = appendLinkFiles(args, p.ExportFile)
need1, need2 := isNeedRuntimeOrPyInit(p)
if !needRuntime { if !needRuntime {
needRuntime = isNeedRuntime(p) needRuntime = need1
}
if !needPyInit {
needPyInit = need2
} }
} }
}) })
var aPkg *aPackage
for _, v := range pkgs {
if v.Package == pkg { // found this package
aPkg = v
break
}
}
dirty := false
if needRuntime && runtimeFiles != nil { if needRuntime && runtimeFiles != nil {
args = append(args, runtimeFiles...) args = append(args, runtimeFiles...)
} else { } else {
for _, aPkg := range pkgs { dirty = true
if aPkg.Package == pkg { // make empty runtime.init if no runtime needed fn := aPkg.LPkg.FuncOf(cl.RuntimeInit)
lpkg := aPkg.LPkg fn.MakeBody(1).Return()
lpkg.FuncOf(cl.RuntimeInit).MakeBody(1).Return() }
if needLLFile(mode) { if needPyInit {
os.WriteFile(pkg.ExportFile, []byte(lpkg.String()), 0644) dirty = aPkg.LPkg.PyInit()
} }
break
} if dirty && needLLFile(mode) {
} lpkg := aPkg.LPkg
os.WriteFile(pkg.ExportFile, []byte(lpkg.String()), 0644)
} }
if verbose || mode != ModeRun { if verbose || mode != ModeRun {

View File

@@ -30,6 +30,7 @@ import (
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
llssa "github.com/goplus/llgo/ssa" llssa "github.com/goplus/llgo/ssa"
cpackages "golang.org/x/tools/go/packages"
) )
func Init() { func Init() {
@@ -71,14 +72,17 @@ func Gen(pkgPath, inFile string, src any) string {
} }
prog := llssa.NewProgram(nil) prog := llssa.NewProgram(nil)
prog.SetRuntime(func() *types.Package { initRtAndPy(prog, &cpackages.Config{
rt, err := imp.Import(llssa.PkgRuntime) Mode: loadSyntax | cpackages.NeedDeps,
check(err)
return rt
}) })
ret, err := cl.NewPackage(prog, ssaPkg, files) ret, err := cl.NewPackage(prog, ssaPkg, files)
check(err) check(err)
if prog.NeedPyInit { // call PyInit if needed
ret.PyInit()
}
return ret.String() return ret.String()
} }

View File

@@ -38,6 +38,27 @@ const (
loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo
) )
func initRtAndPy(prog llssa.Program, cfg *packages.Config) {
var pkgRtAndPy []*packages.Package
load := func() []*packages.Package {
if pkgRtAndPy == nil {
var err error
pkgRtAndPy, err = packages.Load(cfg, llssa.PkgRuntime, llssa.PkgPython)
check(err)
}
return pkgRtAndPy
}
prog.SetRuntime(func() *types.Package {
rt := load()
return rt[0].Types
})
prog.SetPython(func() *types.Package {
rt := load()
return rt[1].Types
})
}
func GenFrom(fileOrPkg string) string { func GenFrom(fileOrPkg string) string {
cfg := &packages.Config{ cfg := &packages.Config{
Mode: loadSyntax | packages.NeedDeps, Mode: loadSyntax | packages.NeedDeps,
@@ -52,11 +73,7 @@ func GenFrom(fileOrPkg string) string {
ssaPkg.Build() ssaPkg.Build()
prog := llssa.NewProgram(nil) prog := llssa.NewProgram(nil)
prog.SetRuntime(func() *types.Package { initRtAndPy(prog, cfg)
rt, err := packages.Load(cfg, llssa.PkgRuntime)
check(err)
return rt[0].Types
})
if Verbose { if Verbose {
ssaPkg.WriteTo(os.Stderr) ssaPkg.WriteTo(os.Stderr)
@@ -65,6 +82,10 @@ func GenFrom(fileOrPkg string) string {
ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax) ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax)
check(err) check(err)
if prog.NeedPyInit { // call PyInit if needed
ret.PyInit()
}
return ret.String() return ret.String()
} }

Binary file not shown.

View File

@@ -77,38 +77,18 @@ func CheckI2Int(v Interface, t *Type) (uintptr, bool) {
return 0, false return 0, false
} }
func I2Float64(v Interface, t *Type) float64 { func I2String(v Interface, t *Type) string {
if v.tab._type == t { if v.tab._type == t {
return bitCastTo64F(uint64(uintptr(v.data))) return *(*string)(v.data)
} }
panic("I2Float64: type mismatch") panic("I2String: type mismatch")
} }
func CheckI2Float64(v Interface, t *Type) (float64, bool) { func CheckI2String(v Interface, t *Type) (string, bool) {
if v.tab._type == t { if v.tab._type == t {
return bitCastTo64F(uint64(uintptr(v.data))), true return *(*string)(v.data), true
} }
return 0, false return "", false
} }
func I2Float32(v Interface, t *Type) float32 {
if v.tab._type == t {
return bitCastTo32F(uint32(uintptr(v.data)))
}
panic("I2Float32: type mismatch")
}
func CheckI2Float32(v Interface, t *Type) (float32, bool) {
if v.tab._type == t {
return bitCastTo32F(uint32(uintptr(v.data))), true
}
return 0, false
}
//go:linkname bitCastTo64F llgo.bitCastTo64F
func bitCastTo64F(uint64) float64
//go:linkname bitCastTo32F llgo.bitCastTo32F
func bitCastTo32F(uint32) float32
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -45,16 +45,6 @@ func NewString(data unsafe.Pointer, len int) String {
return String{data, len} return String{data, len}
} }
// StringLen returns the length of a string.
func StringLen(s String) int {
return s.len
}
// StringData returns the data pointer of a string.
func StringData(s String) unsafe.Pointer {
return s.data
}
// StringCat concatenates two strings. // StringCat concatenates two strings.
func StringCat(a, b String) String { func StringCat(a, b String) String {
n := a.len + b.len n := a.len + b.len

33
py/README.md Normal file
View File

@@ -0,0 +1,33 @@
Linking Python to Go
=====
TODO
## Demo
The `_demo` directory contains our demos (it start with `_` to prevent the `go` command from compiling it):
* [hellopy](_demo/hellopy/hello.go): link Python to Go and say `Hello world`
* [clpy](_demo/clpy/cleval.go): compile Python code and eval.
* [callpy](_demo/callpy/call.go): call Python standard library function `math.sqrt`.
### How to run demos
To run the demos in directory `_demo`, you need to set the `LLGO_LIB_PYTHON` environment variable first. Assuming you use Python 3.12, and the `libpython3.12.so` (or `libpython3.12.dylib` or `python3.12.lib`) file is in the /foo/bar directory, then you need to set `LLGO_LIB_PYTHON` to:
```sh
export LLGO_LIB_PYTHON=/foo/bar/python3.12
```
For example, `/opt/homebrew/Frameworks/Python.framework/Versions/3.12/libpython3.12.dylib` is a typical python lib location under macOS. So we should set it like this:
```sh
export LLGO_LIB_PYTHON=/opt/homebrew/Frameworks/Python.framework/Versions/3.12/python3.12
```
Then you can run the demos in directory `_demo`:
```sh
cd <demo-directory> # eg. cd _demo/hellopy
llgo run .
```

19
py/_demo/callpy/call.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/py"
)
func main() {
py.Initialize()
py.SetProgramName(*c.Argv)
math := py.ImportModule(c.Str("math"))
sqrt := math.GetAttrString(c.Str("sqrt"))
sqrt2 := sqrt.CallOneArg(py.Float(2))
c.Printf(c.Str("sqrt(2) = %f\n"), sqrt2.Float64())
sqrt2.DecRef()
sqrt.DecRef()
math.DecRef()
py.Finalize()
}

23
py/_demo/clpy/cleval.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/py"
)
func main() {
py.Initialize()
py.SetProgramName(*c.Argv)
code := py.CompileString(c.Str(`print('Hello, World!')`), c.Str(`hello.py`), py.EvalInput)
if code != nil {
mod := py.ImportModule(c.Str("__main__"))
gbl := mod.ModuleGetDict()
result := py.EvalCode(code, gbl, nil)
result.DecRef()
mod.DecRef()
code.DecRef()
}
py.Finalize()
}

13
py/_demo/hellopy/hello.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/py"
)
func main() {
py.Initialize()
py.SetProgramName(*c.Argv)
py.RunSimpleString(c.Str(`print('Hello, World!')`))
py.Finalize()
}

25
py/_pyg/module.c Normal file
View File

@@ -0,0 +1,25 @@
#include <stdlib.h>
#include <stdarg.h>
// 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);
}

46
py/bytes.go Normal file
View File

@@ -0,0 +1,46 @@
/*
* 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 py
import (
_ "unsafe"
"github.com/goplus/llgo/c"
)
// https://docs.python.org/3/c-api/bytes.html
// String returns a new bytes object from a C string.
//
//go:linkname FromCStr C.PyBytes_FromString
func FromCStr(s *c.Char) *Object
// FromString returns a new bytes object from a Go string.
func FromString(s string) *Object {
return stringFromStringAndSize(c.GoStringData(s), uintptr(len(s)))
}
//go:linkname stringFromStringAndSize C.PyBytes_FromStringAndSize
func stringFromStringAndSize(s *c.Char, size uintptr) *Object
// CStr returns the content of a bytes object as a C string.
//
// llgo:link (*Object).CStr C.PyBytes_AsString
func (o *Object) CStr() *c.Char { return nil }
// llgo:link (*Object).Strlen C.PyBytes_Size
func (o *Object) Strlen() uintptr { return 0 }

32
py/float.go Normal file
View File

@@ -0,0 +1,32 @@
/*
* 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 py
import (
_ "unsafe"
)
// https://docs.python.org/3/c-api/float.html
//go:linkname Float C.PyFloat_FromDouble
func Float(v float64) *Object
//go:linkname FloatFromSring C.PyFloat_FromString
func FloatFromSring(v *Object) *Object
// llgo:link (*Object).Float64 C.PyFloat_AsDouble
func (o *Object) Float64() float64 { return 0 }

6
py/llgo.cfg Normal file
View File

@@ -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",
]
}

BIN
py/llgo_autogen.lla Normal file

Binary file not shown.

BIN
py/math/llgo_autogen.lla Normal file

Binary file not shown.

30
py/math/math.go Normal file
View File

@@ -0,0 +1,30 @@
/*
* 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 math
import (
_ "unsafe"
"github.com/goplus/llgo/py"
)
const (
LLGoPackage = "py.math"
)
//go:linkname Sqrt py.sqrt
func Sqrt(x *py.Object) *py.Object

56
py/module.go Normal file
View File

@@ -0,0 +1,56 @@
/*
* 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 py
import (
_ "unsafe"
"github.com/goplus/llgo/c"
)
// -----------------------------------------------------------------------------
// This is a wrapper around py.Import which takes a const char* as an argument
// instead of an Object.
//
//go:linkname ImportModule C.PyImport_ImportModule
func ImportModule(name *c.Char) *Object
// This is a higher-level interface that calls the current “import hook function” (with
// an explicit level of 0, meaning absolute import). It invokes the __import__() function
// from the __builtins__ of the current globals. This means that the import is done using
// whatever import hooks are installed in the current environment.
//
// This function always uses absolute imports.
//
//go:linkname Import C.PyImport_Import
func Import(name *Object) *Object
// Return the dictionary object that implements modules namespace; this object is the same
// as the __dict__ attribute of the module object. If module is not a module object (or a
// subtype of a module object), SystemError is raised and nil is returned.
//
// It is recommended extensions use other Module and Object functions rather than directly
// manipulate a modules __dict__.
//
// 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) {}
// -----------------------------------------------------------------------------

82
py/module.ll Normal file
View File

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

158
py/object.go Normal file
View File

@@ -0,0 +1,158 @@
/*
* 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 py
import (
_ "unsafe"
"github.com/goplus/llgo/c"
)
// Object represents a Python object.
type Object struct {
Unused [8]byte
}
// Create a new value based on a format string similar to those accepted by the
// PyArg_Parse* family of functions and a sequence of values. Returns the value or
// nil in the case of an error; an exception will be raised if nil is returned.
// See https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
//
//go:linkname BuildValue C.Py_BuildValue
func BuildValue(format *c.Char, __llgo_va_list ...any) *Object
// llgo:link (*Object).DecRef C.Py_DecRef
func (o *Object) DecRef() {}
// -----------------------------------------------------------------------------
// Retrieve an attribute named attrName from object o. Returns the attribute value on success,
// or nil on failure. This is the equivalent of the Python expression o.attrName.
//
// llgo:link (*Object).GetAttr C.PyObject_GetAttr
func (o *Object) GetAttr(attrName *Object) *Object { return nil }
// llgo:link (*Object).GetAttrString C.PyObject_GetAttrString
func (o *Object) GetAttrString(attrName *c.Char) *Object { return nil }
// -----------------------------------------------------------------------------
// Determine if the object o is callable. Return 1 if the object is callable and
// 0 otherwise. This function always succeeds.
//
// llgo:link (*Object).Callable C.PyCallable_Check
func (o *Object) Callable() c.Int { return 0 }
// Call a callable Python object o, with arguments given by the tuple args, and
// named arguments given by the dictionary kwargs.
//
// args must not be nil; use an empty tuple if no arguments are needed. If no named
// arguments are needed, kwargs can be nil.
//
// Return the result of the call on success, or raise an exception and return nil
// on failure.
//
// This is the equivalent of the Python expression: o(*args, **kwargs).
//
// llgo:link (*Object).Call C.PyObject_Call
func (o *Object) Call(args, kwargs *Object) *Object { return nil }
// Call a callable Python object callable without any arguments. It is the most
// efficient way to call a callable Python object without any argument.
//
// Return the result of the call on success, or raise an exception and return nil
// on failure.
//
// llgo:link (*Object).CallNoArgs C.PyObject_CallNoArgs
func (o *Object) CallNoArgs() *Object { return nil }
// Call a callable Python object callable with exactly 1 positional argument arg
// and no keyword arguments.
//
// Return the result of the call on success, or raise an exception and return nil
// on failure.
//
// llgo:link (*Object).CallOneArg C.PyObject_CallOneArg
func (o *Object) CallOneArg(arg *Object) *Object { return nil }
// Call a callable Python object o, with arguments given by the tuple args. If no
// arguments are needed, then args can be nil.
//
// Return the result of the call on success, or raise an exception and return nil
// on failure.
//
// This is the equivalent of the Python expression: o(*args).
//
// llgo:link (*Object).CallObject C.PyObject_CallObject
func (o *Object) CallObject(callable, args *Object) *Object { return nil }
// Call a callable Python object o, with a variable number of C arguments. The C
// arguments are described using a py.BuildValue style format string. The format
// can be nil, indicating that no arguments are provided.
//
// Return the result of the call on success, or raise an exception and return nil
// on failure.
//
// This is the equivalent of the Python expression: o(*args).
//
// Note that if you only pass PyObject* args, (*Object).CallFunctionObjArgs is a
// faster alternative.
//
// llgo:link (*Object).CallFunction C.PyObject_CallFunction
func (o *Object) CallFunction(format *c.Char, __llgo_va_list ...any) *Object { return nil }
// Call a callable Python object o, with a variable number of PyObject* arguments.
// The arguments are provided as a variable number of parameters followed by nil.
//
// Return the result of the call on success, or raise an exception and return nil
// on failure.
//
// This is the equivalent of the Python expression: o(arg1, arg2, ...).
//
// llgo:link (*Object).CallFunctionObjArgs C.PyObject_CallFunctionObjArgs
func (o *Object) CallFunctionObjArgs(__llgo_va_list ...any) *Object { return nil }
// llgo:link (*Object).CallMethod C.PyObject_CallMethod
func (o *Object) CallMethod(name *c.Char, format *c.Char, __llgo_va_list ...any) *Object {
return nil
}
// llgo:link (*Object).CallMethodObjArgs C.PyObject_CallMethodObjArgs
func (o *Object) CallMethodObjArgs(name *Object, __llgo_va_list ...any) *Object { return nil }
// llgo:link (*Object).CallMethodNoArgs C.PyObject_CallMethodNoArgs
func (o *Object) CallMethodNoArgs(name *Object) *Object { return nil }
// llgo:link (*Object).CallMethodOneArg C.PyObject_CallMethodOneArg
func (o *Object) CallMethodOneArg(name, arg *Object) *Object { return nil }
// llgo:link (*Object).Vectorcall C.PyObject_Vectorcall
func (o *Object) Vectorcall(args **Object, nargs uintptr, kwnames *Object) *Object {
return nil
}
// llgo:link (*Object).VectorcallDict C.PyObject_VectorcallDict
func (o *Object) VectorcallDict(args **Object, nargs uintptr, kwdict *Object) *Object {
return nil
}
// llgo:link (*Object).VectorcallMethod C.PyObject_VectorcallMethod
func (o *Object) VectorcallMethod(name *Object, args **Object, nargs uintptr, kwnames *Object) *Object {
return nil
}
// -----------------------------------------------------------------------------

BIN
py/os/llgo_autogen.lla Normal file

Binary file not shown.

30
py/os/os.go Normal file
View File

@@ -0,0 +1,30 @@
/*
* 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 os
import (
_ "unsafe"
"github.com/goplus/llgo/py"
)
const (
LLGoPackage = "py.os"
)
//go:linkname Getcwd py.getcwd
func Getcwd() *py.Object

120
py/python.go Normal file
View File

@@ -0,0 +1,120 @@
/*
* 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 py
import (
_ "unsafe"
"github.com/goplus/llgo/c"
)
const (
LLGoPackage = "link: $LLGO_LIB_PYTHON"
)
// -----------------------------------------------------------------------------
//go:linkname SetProgramName C.Py_SetProgramName
func SetProgramName(name *c.Char)
//go:linkname Initialize C.Py_Initialize
func Initialize()
// This function works like Initialize() if initsigs is 1.
// If initsigs is 0, it skips initialization registration of signal handlers,
// which might be useful when Python is embedded.
//
//go:linkname InitializeEx C.Py_InitializeEx
func InitializeEx(initsigs c.Int)
//go:linkname Finalize C.Py_Finalize
func Finalize()
// -----------------------------------------------------------------------------
//go:linkname RunSimpleString C.PyRun_SimpleString
func RunSimpleString(command *c.Char) c.Int
//go:linkname RunSimpleStringFlags C.PyRun_SimpleStringFlags
func RunSimpleStringFlags(command *c.Char, flags *CompilerFlags) c.Int
//go:linkname RunSimpleFile C.PyRun_SimpleFile
func RunSimpleFile(fp c.FilePtr, filename *c.Char) c.Int
//go:linkname RunSimpleFileFlags C.PyRun_SimpleFileFlags
func RunSimpleFileFlags(fp c.FilePtr, filename *c.Char, flags *CompilerFlags) c.Int
// -----------------------------------------------------------------------------
type InputType c.Int
const (
SingleInput InputType = 256 // read code from i/o
FileInput InputType = 257 // read code from filename
EvalInput InputType = 258 // read code from string
// FuncTypeInput InputType = 345
)
// llgo:type C
type CompilerFlags struct {
CfFlags c.Int
}
//go:linkname CompileString C.Py_CompileString
func CompileString(str, filename *c.Char, start InputType) *Object
//go:linkname CompileStringFlags C.Py_CompileStringFlags
func CompileStringFlags(str, filename *c.Char, start InputType, flags *CompilerFlags) *Object
//go:linkname CompileStringExFlags C.Py_CompileStringExFlags
func CompileStringExFlags(str, filename *c.Char, start InputType, flags *CompilerFlags, optimize c.Int) *Object
// Parse and compile the Python source code in str, returning the resulting code object.
// The start token is given by start; this can be used to constrain the code which can be
// compiled and should be py.EvalInput, py.FileInput, or py.SingleInput. The filename
// specified by filename is used to construct the code object and may appear in tracebacks
// or SyntaxError exception messages. This returns NULL if the code cannot be parsed or
// compiled.
//
// The integer optimize specifies the optimization level of the compiler; a value of -1
// selects the optimization level of the interpreter as given by -O options. Explicit levels
// are 0 (no optimization; __debug__ is true), 1 (asserts are removed, __debug__ is false) or
// 2 (docstrings are removed too).
//
//go:linkname CompileStringObject C.Py_CompileStringObject
func CompileStringObject(str *c.Char, filename *Object, start InputType, flags *CompilerFlags, optimize c.Int) *Object
// -----------------------------------------------------------------------------
// This is a simplified interface to EvalCodeEx, with just the code object, and global and
// local variables. The other arguments are set to nil.
//
//go:linkname EvalCode C.PyEval_EvalCode
func EvalCode(code, globals, locals *Object) *Object
// Evaluate a precompiled code object, given a particular environment for its evaluation.
// This environment consists of a dictionary of global variables, a mapping object of local
// variables, arrays of arguments, keywords and defaults, a dictionary of default values for
// keyword-only arguments and a closure tuple of cells.
//
//go:linkname EvalCodeEx C.PyEval_EvalCodeEx
func EvalCodeEx(
code, globals, locals *Object,
args *Object, argcount c.Int, kws *Object, kwcount c.Int,
defs *Object, defcount c.Int, kwdefs, closure *Object) *Object
// -----------------------------------------------------------------------------

View File

@@ -22,6 +22,10 @@ import (
"github.com/goplus/llgo/cl/cltest" "github.com/goplus/llgo/cl/cltest"
) )
func TestFromTestpy(t *testing.T) {
cltest.FromDir(t, "", "../cl/_testpy", false)
}
func TestFromTestrt(t *testing.T) { func TestFromTestrt(t *testing.T) {
cltest.FromDir(t, "", "../cl/_testrt", true) cltest.FromDir(t, "", "../cl/_testrt", true)
} }

View File

@@ -18,6 +18,7 @@ package ssa
import ( import (
"go/types" "go/types"
"log"
"strconv" "strconv"
"github.com/goplus/llvm" "github.com/goplus/llvm"
@@ -68,6 +69,23 @@ type aGlobal struct {
// variable. // variable.
type Global = *aGlobal type Global = *aGlobal
// NewVar creates a new global variable.
func (p Package) NewVar(name string, typ types.Type, bg Background) Global {
if v, ok := p.vars[name]; ok {
return v
}
t := p.Prog.Type(typ, bg)
gbl := llvm.AddGlobal(p.mod, t.ll, name)
ret := &aGlobal{Expr{gbl, t}}
p.vars[name] = ret
return ret
}
// VarOf returns a global variable by name.
func (p Package) VarOf(name string) Global {
return p.vars[name]
}
// Init initializes the global variable with the given value. // Init initializes the global variable with the given value.
func (g Global) Init(v Expr) { func (g Global) Init(v Expr) {
g.impl.SetInitializer(v.impl) g.impl.SetInitializer(v.impl)
@@ -140,6 +158,31 @@ type aFunction struct {
// Function represents a function or method. // Function represents a function or method.
type Function = *aFunction type Function = *aFunction
// NewFunc creates a new function.
func (p Package) NewFunc(name string, sig *types.Signature, bg Background) Function {
return p.NewFuncEx(name, sig, bg, false)
}
// NewFuncEx creates a new function.
func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, hasFreeVars bool) Function {
if v, ok := p.fns[name]; ok {
return v
}
t := p.Prog.FuncDecl(sig, bg)
if debugInstr {
log.Println("NewFunc", name, t.raw.Type, "hasFreeVars:", hasFreeVars)
}
fn := llvm.AddFunction(p.mod, name, t.ll)
ret := newFunction(fn, t, p, p.Prog, hasFreeVars)
p.fns[name] = ret
return ret
}
// FuncOf returns a function by name.
func (p Package) FuncOf(name string) Function {
return p.fns[name]
}
func newFunction(fn llvm.Value, t Type, pkg Package, prog Program, hasFreeVars bool) Function { func newFunction(fn llvm.Value, t Type, pkg Package, prog Program, hasFreeVars bool) Function {
params, hasVArg := newParams(t, prog) params, hasVArg := newParams(t, prog)
base := 0 base := 0
@@ -212,22 +255,71 @@ func (p Function) MakeBody(nblk int) Builder {
// MakeBlocks creates nblk basic blocks for the function. // MakeBlocks creates nblk basic blocks for the function.
func (p Function) MakeBlocks(nblk int) []BasicBlock { func (p Function) MakeBlocks(nblk int) []BasicBlock {
if p.blks == nil { n := len(p.blks)
if n == 0 {
p.blks = make([]BasicBlock, 0, nblk) p.blks = make([]BasicBlock, 0, nblk)
} }
n := len(p.blks)
f := p.impl
for i := 0; i < nblk; i++ { for i := 0; i < nblk; i++ {
label := "_llgo_" + strconv.Itoa(i) p.addBlock(n + i)
blk := llvm.AddBasicBlock(f, label)
p.blks = append(p.blks, &aBasicBlock{blk, p, n + i})
} }
return p.blks[n:] return p.blks[n:]
} }
func (p Function) addBlock(idx int) BasicBlock {
label := "_llgo_" + strconv.Itoa(idx)
blk := llvm.AddBasicBlock(p.impl, label)
ret := &aBasicBlock{blk, p, idx}
p.blks = append(p.blks, ret)
return ret
}
// MakeBlock creates a new basic block for the function.
func (p Function) MakeBlock() BasicBlock {
return p.addBlock(len(p.blks))
}
// Block returns the ith basic block of the function. // Block returns the ith basic block of the function.
func (p Function) Block(idx int) BasicBlock { func (p Function) Block(idx int) BasicBlock {
return p.blks[idx] return p.blks[idx]
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type aPyObjRef struct {
Expr
Obj Global
}
// 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) PyObjRef {
if v, ok := p.pyobjs[name]; ok {
return v
}
prog := p.Prog
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{types.NewPointer(sig)}, vkPyFuncRef}
expr := Expr{obj.impl, ty}
ret := &aPyObjRef{expr, obj}
p.pyobjs[name] = ret
return ret
}
// 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
}
// -----------------------------------------------------------------------------

View File

@@ -24,6 +24,7 @@ import (
"go/types" "go/types"
"log" "log"
"github.com/goplus/llgo/internal/abi"
"github.com/goplus/llvm" "github.com/goplus/llvm"
) )
@@ -257,6 +258,11 @@ var floatPredOpToLLVM = []llvm.FloatPredicate{
token.GEQ - predOpBase: llvm.FloatOGE, token.GEQ - predOpBase: llvm.FloatOGE,
} }
var boolPredOpToLLVM = []llvm.IntPredicate{
token.EQL - predOpBase: llvm.IntEQ,
token.NEQ - predOpBase: llvm.IntNE,
}
// EQL NEQ LSS LEQ GTR GEQ == != < <= < >= // EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
func isPredOp(op token.Token) bool { func isPredOp(op token.Token) bool {
return op >= predOpBase && op <= predOpLast return op >= predOpBase && op <= predOpLast
@@ -289,7 +295,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
} }
case isLogicOp(op): // op: & | ^ << >> &^ case isLogicOp(op): // op: & | ^ << >> &^
if op == token.AND_NOT { if op == token.AND_NOT {
panic("todo") return Expr{b.impl.CreateAnd(x.impl, b.impl.CreateNot(y.impl, ""), ""), x.Type}
} }
kind := x.kind kind := x.kind
llop := logicOpToLLVM[op-logicOpBase] llop := logicOpToLLVM[op-logicOpBase]
@@ -310,7 +316,10 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
case vkFloat: case vkFloat:
pred := floatPredOpToLLVM[op-predOpBase] pred := floatPredOpToLLVM[op-predOpBase]
return Expr{llvm.CreateFCmp(b.impl, pred, x.impl, y.impl), tret} return Expr{llvm.CreateFCmp(b.impl, pred, x.impl, y.impl), tret}
case vkString, vkComplex, vkBool: case vkBool:
pred := boolPredOpToLLVM[op-predOpBase]
return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret}
case vkString, vkComplex:
panic("todo") panic("todo")
} }
} }
@@ -598,6 +607,26 @@ func (b Builder) getField(x Expr, idx int) Expr {
return Expr{fld, tfld} return Expr{fld, tfld}
} }
// StringData returns the data pointer of a string.
func (b Builder) StringData(x Expr) Expr {
if debugInstr {
log.Printf("StringData %v\n", x.impl)
}
prog := b.Prog
ptr := llvm.CreateExtractValue(b.impl, x.impl, 0)
return Expr{ptr, prog.CStr()}
}
// StringLen returns the length of a string.
func (b Builder) StringLen(x Expr) Expr {
if debugInstr {
log.Printf("StringLen %v\n", x.impl)
}
prog := b.Prog
ptr := llvm.CreateExtractValue(b.impl, x.impl, 1)
return Expr{ptr, prog.Int()}
}
// The IndexAddr instruction yields the address of the element at // The IndexAddr instruction yields the address of the element at
// index `idx` of collection `x`. `idx` is an integer expression. // index `idx` of collection `x`. `idx` is an integer expression.
// //
@@ -649,8 +678,7 @@ func (b Builder) Index(x, idx Expr, addr func(Expr) Expr) Expr {
panic(fmt.Errorf("invalid operation: cannot index %v", t)) panic(fmt.Errorf("invalid operation: cannot index %v", t))
} }
telem = prog.rawType(types.Typ[types.Byte]) telem = prog.rawType(types.Typ[types.Byte])
pkg := b.Func.Pkg ptr = b.StringData(x)
ptr = b.InlineCall(pkg.rtFunc("StringData"), x)
case *types.Array: case *types.Array:
telem = prog.Index(x.Type) telem = prog.Index(x.Type)
if addr != nil { if addr != nil {
@@ -717,7 +745,7 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
panic(fmt.Errorf("invalid operation: cannot slice %v", t)) panic(fmt.Errorf("invalid operation: cannot slice %v", t))
} }
if high.IsNil() { if high.IsNil() {
high = b.InlineCall(pkg.rtFunc("StringLen"), x) high = b.StringLen(x)
} }
ret.Type = x.Type ret.Type = x.Type
ret.impl = b.InlineCall(pkg.rtFunc("NewStringSlice"), x, low, high).impl ret.impl = b.InlineCall(pkg.rtFunc("NewStringSlice"), x, low, high).impl
@@ -871,7 +899,7 @@ func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
log.Printf("AllocaCStr %v\n", gostr.impl) log.Printf("AllocaCStr %v\n", gostr.impl)
} }
pkg := b.Func.Pkg pkg := b.Func.Pkg
n := b.InlineCall(pkg.rtFunc("StringLen"), gostr) n := b.StringLen(gostr)
n1 := b.BinOp(token.ADD, n, b.Prog.Val(1)) n1 := b.BinOp(token.ADD, n, b.Prog.Val(1))
cstr := b.Alloca(n1) cstr := b.Alloca(n1)
return b.InlineCall(pkg.rtFunc("CStrCopy"), cstr, gostr) return b.InlineCall(pkg.rtFunc("CStrCopy"), cstr, gostr)
@@ -1051,7 +1079,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) {
case *types.Basic: case *types.Basic:
kind := tx.Kind() kind := tx.Kind()
switch { switch {
case kind >= types.Int && kind <= types.Uintptr: case kind >= types.Bool && kind <= types.Uintptr:
t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind))) t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind)))
tptr := prog.Uintptr() tptr := prog.Uintptr()
vptr := Expr{llvm.CreateIntCast(b.impl, x.impl, tptr.ll), tptr} vptr := Expr{llvm.CreateIntCast(b.impl, x.impl, tptr.ll), tptr}
@@ -1118,7 +1146,7 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.raw.Type, commaOk) log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.raw.Type, commaOk)
} }
switch assertedTyp.kind { switch assertedTyp.kind {
case vkSigned, vkUnsigned: case vkSigned, vkUnsigned, vkFloat, vkBool:
pkg := b.Func.Pkg pkg := b.Func.Pkg
fnName := "I2Int" fnName := "I2Int"
if commaOk { if commaOk {
@@ -1133,25 +1161,44 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
panic("todo") panic("todo")
} }
typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind))) typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind)))
return b.InlineCall(fn, x, typ) ret = b.InlineCall(fn, x, typ)
case vkFloat: if kind != types.Uintptr {
var fnName string conv := func(v llvm.Value) llvm.Value {
kind := assertedTyp.raw.Underlying().(*types.Basic).Kind() switch kind {
switch kind { case types.Float32:
case types.Float32: v = castInt(b.impl, v, b.Prog.tyInt32())
fnName = "I2Float32" v = b.impl.CreateBitCast(v, assertedTyp.ll, "")
if commaOk { case types.Float64:
fnName = "CheckI2Float32" v = b.impl.CreateBitCast(v, assertedTyp.ll, "")
default:
v = castInt(b.impl, v, assertedTyp.ll)
}
return v
} }
case types.Float64: if !commaOk {
fnName = "I2Float64" ret.Type = assertedTyp
if commaOk { ret.impl = conv(ret.impl)
fnName = "CheckI2Float64" } else {
ret.Type = b.Prog.toTuple(
types.NewTuple(
types.NewVar(token.NoPos, nil, "", assertedTyp.RawType()),
ret.Type.RawType().(*types.Tuple).At(1),
),
)
val0 := conv(b.impl.CreateExtractValue(ret.impl, 0, ""))
val1 := b.impl.CreateExtractValue(ret.impl, 1, "")
ret.impl = llvm.ConstStruct([]llvm.Value{val0, val1}, false)
} }
} }
return
case vkString:
pkg := b.Func.Pkg pkg := b.Func.Pkg
fnName := "I2String"
if commaOk {
fnName = "CheckI2String"
}
fn := pkg.rtFunc(fnName) fn := pkg.rtFunc(fnName)
typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind))) typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(abi.String)))
return b.InlineCall(fn, x, typ) return b.InlineCall(fn, x, typ)
} }
panic("todo") panic("todo")
@@ -1176,7 +1223,6 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
// t4 = t3() // t4 = t3()
// t7 = invoke t5.Println(...t6) // t7 = invoke t5.Println(...t6)
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
prog := b.Prog
if debugInstr { if debugInstr {
var b bytes.Buffer var b bytes.Buffer
name := fn.impl.Name() name := fn.impl.Name()
@@ -1191,11 +1237,16 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
} }
log.Println(b.String()) log.Println(b.String())
} }
var kind = fn.kind
if kind == vkPyFuncRef {
return b.pyCall(fn, args)
}
var ll llvm.Type var ll llvm.Type
var data Expr var data Expr
var sig *types.Signature var sig *types.Signature
var prog = b.Prog
var raw = fn.raw.Type var raw = fn.raw.Type
switch fn.kind { switch kind {
case vkClosure: case vkClosure:
data = b.Field(fn, 1) data = b.Field(fn, 1)
fn = b.Field(fn, 0) fn = b.Field(fn, 0)
@@ -1246,7 +1297,7 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
return b.InlineCall(b.Func.Pkg.rtFunc("SliceLen"), arg) return b.InlineCall(b.Func.Pkg.rtFunc("SliceLen"), arg)
case *types.Basic: case *types.Basic:
if t.Kind() == types.String { if t.Kind() == types.String {
return b.InlineCall(b.Func.Pkg.rtFunc("StringLen"), arg) return b.StringLen(arg)
} }
} }
} }
@@ -1262,11 +1313,4 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
panic("todo") panic("todo")
} }
// BitCast bit cast expr to type
func (b Builder) BitCast(val Expr, typ Type) (ret Expr) {
ret.Type = typ
ret.impl = b.impl.CreateBitCast(val.impl, typ.ll, "")
return
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -19,13 +19,13 @@ package ssa
import ( import (
"go/token" "go/token"
"go/types" "go/types"
"log"
"github.com/goplus/llvm" "github.com/goplus/llvm"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )
const ( const (
PkgPython = "github.com/goplus/llgo/py"
PkgRuntime = "github.com/goplus/llgo/internal/runtime" PkgRuntime = "github.com/goplus/llgo/internal/runtime"
) )
@@ -103,9 +103,13 @@ type aProgram struct {
rt *types.Package rt *types.Package
rtget func() *types.Package rtget func() *types.Package
py *types.Package
pyget func() *types.Package
target *Target target *Target
td llvm.TargetData td llvm.TargetData
// tm llvm.TargetMachine // tm llvm.TargetMachine
named map[string]llvm.Type
intType llvm.Type intType llvm.Type
int1Type llvm.Type int1Type llvm.Type
@@ -130,8 +134,16 @@ type aProgram struct {
uintptrTy Type uintptrTy Type
intTy Type intTy Type
f64Ty Type f64Ty Type
pyObjPtr Type
pyObjPPtr Type
needRuntime bool pyImpTy *types.Signature
callNoArgs *types.Signature
callOneArg *types.Signature
loadPyModS *types.Signature
NeedRuntime bool
NeedPyInit bool
} }
// A Program presents a program. // A Program presents a program.
@@ -154,7 +166,18 @@ func NewProgram(target *Target) Program {
// TODO(xsw): Finalize may cause panic, so comment it. // TODO(xsw): Finalize may cause panic, so comment it.
ctx.Finalize() ctx.Finalize()
*/ */
return &aProgram{ctx: ctx, gocvt: newGoTypes(), target: target, td: td} return &aProgram{ctx: ctx, gocvt: newGoTypes(), target: target, td: td, named: make(map[string]llvm.Type)}
}
// SetPython sets the Python package.
// Its type can be *types.Package or func() *types.Package.
func (p Program) SetPython(py any) {
switch v := py.(type) {
case *types.Package:
p.py = v
case func() *types.Package:
p.pyget = v
}
} }
// SetRuntime sets the runtime. // SetRuntime sets the runtime.
@@ -168,25 +191,33 @@ func (p Program) SetRuntime(runtime any) {
} }
} }
// NeedRuntime returns if the current package needs runtime.
func (p Program) NeedRuntime() bool {
return p.needRuntime
}
func (p Program) runtime() *types.Package { func (p Program) runtime() *types.Package {
if p.rt == nil { if p.rt == nil {
p.rt = p.rtget() p.rt = p.rtget()
} }
p.needRuntime = true p.NeedRuntime = true
return p.rt return p.rt
} }
func (p Program) python() *types.Package {
if p.py == nil {
p.py = p.pyget()
}
return p.py
}
func (p Program) rtNamed(name string) *types.Named { func (p Program) rtNamed(name string) *types.Named {
t := p.runtime().Scope().Lookup(name).Type().(*types.Named) t := p.runtime().Scope().Lookup(name).Type().(*types.Named)
t, _ = p.gocvt.cvtNamed(t) t, _ = p.gocvt.cvtNamed(t)
return t return t
} }
func (p Program) pyNamed(name string) *types.Named {
// TODO(xsw): does python type need to convert?
t := p.python().Scope().Lookup(name).Type().(*types.Named)
return t
}
func (p Program) rtType(name string) Type { func (p Program) rtType(name string) Type {
return p.rawType(p.rtNamed(name)) return p.rawType(p.rtNamed(name))
} }
@@ -227,8 +258,29 @@ func (p Program) NewPackage(name, pkgPath string) Package {
gbls := make(map[string]Global) gbls := make(map[string]Global)
fns := make(map[string]Function) fns := make(map[string]Function)
stubs := make(map[string]Function) stubs := make(map[string]Function)
p.needRuntime = false pyobjs := make(map[string]PyObjRef)
return &aPackage{mod, gbls, fns, stubs, p} pymods := make(map[string]Global)
p.NeedRuntime = false
// Don't need reset p.needPyInit here
// p.needPyInit = false
return &aPackage{mod, gbls, fns, stubs, pyobjs, pymods, p}
}
// PyObjectPtrPtr returns the **py.Object type.
func (p Program) PyObjectPtrPtr() Type {
if p.pyObjPPtr == nil {
p.pyObjPPtr = p.Pointer(p.PyObjectPtr())
}
return p.pyObjPPtr
}
// PyObjectPtr returns the *py.Object type.
func (p Program) PyObjectPtr() Type {
if p.pyObjPtr == nil {
objPtr := types.NewPointer(p.pyNamed("Object"))
p.pyObjPtr = p.rawType(objPtr)
}
return p.pyObjPtr
} }
// Void returns void type. // Void returns void type.
@@ -311,11 +363,13 @@ func (p Program) Float64() Type {
// initializer) and "init#%d", the nth declared init function, // initializer) and "init#%d", the nth declared init function,
// and unspecified other things too. // and unspecified other things too.
type aPackage struct { type aPackage struct {
mod llvm.Module mod llvm.Module
vars map[string]Global vars map[string]Global
fns map[string]Function fns map[string]Function
stubs map[string]Function stubs map[string]Function
Prog Program pyobjs map[string]PyObjRef
pymods map[string]Global
Prog Program
} }
type Package = *aPackage type Package = *aPackage
@@ -327,40 +381,6 @@ func (p Package) NewConst(name string, val constant.Value) NamedConst {
} }
*/ */
// NewVar creates a new global variable.
func (p Package) NewVar(name string, typ types.Type, bg Background) Global {
t := p.Prog.Type(typ, bg)
gbl := llvm.AddGlobal(p.mod, t.ll, name)
ret := &aGlobal{Expr{gbl, t}}
p.vars[name] = ret
return ret
}
// VarOf returns a global variable by name.
func (p Package) VarOf(name string) Global {
return p.vars[name]
}
// NewFunc creates a new function.
func (p Package) NewFunc(name string, sig *types.Signature, bg Background) Function {
return p.NewFuncEx(name, sig, bg, false)
}
// NewFuncEx creates a new function.
func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, hasFreeVars bool) Function {
if v, ok := p.fns[name]; ok {
return v
}
t := p.Prog.FuncDecl(sig, bg)
if debugInstr {
log.Println("NewFunc", name, t.raw.Type, "hasFreeVars:", hasFreeVars)
}
fn := llvm.AddFunction(p.mod, name, t.ll)
ret := newFunction(fn, t, p, p.Prog, hasFreeVars)
p.fns[name] = ret
return ret
}
func (p Package) rtFunc(fnName string) Expr { func (p Package) rtFunc(fnName string) Expr {
fn := p.Prog.runtime().Scope().Lookup(fnName).(*types.Func) fn := p.Prog.runtime().Scope().Lookup(fnName).(*types.Func)
name := FullName(fn.Pkg(), fnName) name := FullName(fn.Pkg(), fnName)
@@ -368,6 +388,11 @@ func (p Package) rtFunc(fnName string) Expr {
return p.NewFunc(name, sig, InGo).Expr return p.NewFunc(name, sig, InGo).Expr
} }
func (p Package) pyFunc(fullName string, sig *types.Signature) Expr {
p.Prog.NeedPyInit = true
return p.NewFunc(fullName, sig, InC).Expr
}
func (p Package) closureStub(b Builder, t *types.Struct, v Expr) Expr { func (p Package) closureStub(b Builder, t *types.Struct, v Expr) Expr {
name := v.impl.Name() name := v.impl.Name()
prog := b.Prog prog := b.Prog
@@ -401,11 +426,6 @@ func (p Package) closureStub(b Builder, t *types.Struct, v Expr) Expr {
return b.aggregateValue(prog.rawType(t), v.impl, nilVal) return b.aggregateValue(prog.rawType(t), v.impl, nilVal)
} }
// FuncOf returns a function by name.
func (p Package) FuncOf(name string) Function {
return p.fns[name]
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// String returns a string representation of the package. // String returns a string representation of the package.
@@ -454,3 +474,131 @@ func (p *Package) WriteFile(file string) (err error) {
*/ */
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
func (p Program) tyImportPyModule() *types.Signature {
if p.pyImpTy == nil {
charPtr := types.NewPointer(types.Typ[types.Int8])
objPtr := p.PyObjectPtr().raw.Type
params := types.NewTuple(types.NewParam(token.NoPos, nil, "", charPtr))
results := types.NewTuple(types.NewParam(token.NoPos, nil, "", objPtr))
p.pyImpTy = types.NewSignatureType(nil, nil, nil, params, results, false)
}
return p.pyImpTy
}
func (p Program) tyCallNoArgs() *types.Signature {
if p.callNoArgs == nil {
objPtr := p.PyObjectPtr().raw.Type
paramObjPtr := types.NewParam(token.NoPos, nil, "", objPtr)
params := types.NewTuple(paramObjPtr)
p.callNoArgs = types.NewSignatureType(nil, nil, nil, params, params, false)
}
return p.callNoArgs
}
func (p Program) tyCallOneArg() *types.Signature {
if p.callOneArg == nil {
objPtr := p.PyObjectPtr().raw.Type
paramObjPtr := types.NewParam(token.NoPos, nil, "", objPtr)
params := types.NewTuple(paramObjPtr, paramObjPtr)
results := types.NewTuple(paramObjPtr)
p.callOneArg = types.NewSignatureType(nil, nil, nil, params, results, false)
}
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 {
b := fn.NewBuilder()
b.SetBlockEx(fn.Block(0), AtStart).CallPyInit()
b.Dispose()
return true
}
return false
}
// NewPyModVar creates a new global variable for a Python module.
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)
if doInit {
g.Init(prog.Null(g.Type))
g.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
}
p.pymods[name] = g
return g
}
// ImportPyMod imports a Python module.
func (b Builder) ImportPyMod(path string) Expr {
pkg := b.Func.Pkg
fnImp := pkg.pyFunc("PyImport_ImportModule", b.Prog.tyImportPyModule())
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()
switch n {
case 0:
call := pkg.pyFunc("PyObject_CallNoArgs", prog.tyCallNoArgs())
ret = b.Call(call, fn)
case 1:
call := pkg.pyFunc("PyObject_CallOneArg", prog.tyCallOneArg())
ret = b.Call(call, fn, args[0])
default:
panic("todo")
}
return
}
// CallPyInit calls Py_Initialize.
func (b Builder) CallPyInit() (ret Expr) {
fn := b.Func.Pkg.pyFunc("Py_Initialize", NoArgsNoRet)
return b.Call(fn)
}
var (
NoArgsNoRet = types.NewSignatureType(nil, nil, nil, nil, nil, false)
)
// -----------------------------------------------------------------------------

View File

@@ -25,6 +25,34 @@ import (
"github.com/goplus/llvm" "github.com/goplus/llvm"
) )
func TestSetBlock(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Log("SetBlock: no error?")
}
}()
fn := &aFunction{}
b := &aBuilder{Func: fn}
b.SetBlock(&aBasicBlock{})
}
func TestSetBlockEx(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Log("SetBlockEx: no error?")
}
}()
fn := &aFunction{}
b := &aBuilder{Func: fn}
b.SetBlockEx(&aBasicBlock{fn: fn}, -1)
}
func TestSetPython(t *testing.T) {
prog := NewProgram(nil)
typ := types.NewPackage("foo", "foo")
prog.SetPython(typ)
}
func TestClosureCtx(t *testing.T) { func TestClosureCtx(t *testing.T) {
defer func() { defer func() {
if r := recover(); r == nil { if r := recover(); r == nil {
@@ -106,10 +134,32 @@ func assertPkg(t *testing.T, p Package, expected string) {
} }
} }
func TestPyFunc(t *testing.T) {
prog := NewProgram(nil)
py := types.NewPackage("foo", "foo")
o := types.NewTypeName(0, py, "Object", nil)
types.NewNamed(o, types.Typ[types.Int], nil)
py.Scope().Insert(o)
prog.SetPython(py)
pkg := prog.NewPackage("bar", "foo/bar")
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
a := pkg.NewPyFunc("a", sig, false)
if pkg.NewPyFunc("a", sig, false) != a {
t.Fatal("NewPyFunc(a) failed")
}
foo := pkg.NewPyModVar("foo", false)
if pkg.NewPyModVar("foo", false) != foo {
t.Fatal("NewPyModVar(foo) failed")
}
}
func TestVar(t *testing.T) { func TestVar(t *testing.T) {
prog := NewProgram(nil) prog := NewProgram(nil)
pkg := prog.NewPackage("bar", "foo/bar") pkg := prog.NewPackage("bar", "foo/bar")
a := pkg.NewVar("a", types.Typ[types.Int], InGo) a := pkg.NewVar("a", types.Typ[types.Int], InGo)
if pkg.NewVar("a", types.Typ[types.Int], InGo) != a {
t.Fatal("NewVar(a) failed")
}
a.Init(prog.Val(100)) a.Init(prog.Val(100))
b := pkg.NewVar("b", types.Typ[types.Int], InGo) b := pkg.NewVar("b", types.Typ[types.Int], InGo)
b.Init(a.Expr) b.Init(a.Expr)
@@ -149,7 +199,7 @@ source_filename = "foo/bar"
@a = external global {} @a = external global {}
`) `)
if prog.NeedRuntime() { if prog.NeedRuntime {
t.Fatal("NeedRuntime?") t.Fatal("NeedRuntime?")
} }
} }

View File

@@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"go/types" "go/types"
"log" "log"
"strings"
"github.com/goplus/llvm" "github.com/goplus/llvm"
) )
@@ -57,18 +58,67 @@ type aBuilder struct {
// Builder represents a builder for creating instructions in a function. // Builder represents a builder for creating instructions in a function.
type Builder = *aBuilder type Builder = *aBuilder
// SetBlock sets the current block to the specified basic block. // Dispose disposes of the builder.
func (b Builder) Dispose() {
b.impl.Dispose()
}
// SetBlock means SetBlockEx(blk, AtEnd).
func (b Builder) SetBlock(blk BasicBlock) Builder { func (b Builder) SetBlock(blk BasicBlock) Builder {
if b.Func != blk.fn {
panic("mismatched function")
}
if debugInstr { if debugInstr {
log.Printf("Block _llgo_%v:\n", blk.idx) log.Printf("Block _llgo_%v:\n", blk.idx)
} }
b.impl.SetInsertPointAtEnd(blk.impl) b.SetBlockEx(blk, AtEnd)
return b return b
} }
type InsertPoint int
const (
AtEnd InsertPoint = iota
AtStart
AfterInit
)
// SetBlockEx sets blk as current basic block and pos as its insert point.
func (b Builder) SetBlockEx(blk BasicBlock, pos InsertPoint) Builder {
if b.Func != blk.fn {
panic("mismatched function")
}
switch pos {
case AtEnd:
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. // Panic emits a panic instruction.
func (b Builder) Panic(v Expr) { func (b Builder) Panic(v Expr) {
if debugInstr { if debugInstr {

View File

@@ -43,6 +43,7 @@ const (
vkFuncDecl vkFuncDecl
vkFuncPtr vkFuncPtr
vkClosure vkClosure
vkPyFuncRef
vkTuple vkTuple
vkPhisExpr = -1 vkPhisExpr = -1
) )
@@ -188,6 +189,10 @@ func (p Program) tyInt64() llvm.Type {
return p.int64Type return p.int64Type
} }
func (p Program) toTuple(typ *types.Tuple) Type {
return &aType{p.toLLVMTuple(typ), rawType{typ}, vkTuple}
}
func (p Program) toType(raw types.Type) Type { func (p Program) toType(raw types.Type) Type {
typ := rawType{raw} typ := rawType{raw}
switch t := raw.(type) { switch t := raw.(type) {
@@ -251,7 +256,11 @@ func (p Program) toType(raw types.Type) Type {
} }
func (p Program) toLLVMNamedStruct(name string, raw *types.Struct) llvm.Type { func (p Program) toLLVMNamedStruct(name string, raw *types.Struct) llvm.Type {
if typ, ok := p.named[name]; ok {
return typ
}
t := p.ctx.StructCreateNamed(name) t := p.ctx.StructCreateNamed(name)
p.named[name] = t
fields := p.toLLVMFields(raw) fields := p.toLLVMFields(raw)
t.StructSetBody(fields, false) t.StructSetBody(fields, false)
return t return t

View File

@@ -26,12 +26,14 @@ import (
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type goTypes struct { type goTypes struct {
typs map[unsafe.Pointer]unsafe.Pointer typs map[unsafe.Pointer]unsafe.Pointer
named map[string]*types.Named
} }
func newGoTypes() goTypes { func newGoTypes() goTypes {
typs := make(map[unsafe.Pointer]unsafe.Pointer) typs := make(map[unsafe.Pointer]unsafe.Pointer)
return goTypes{typs} named := make(map[string]*types.Named)
return goTypes{typs, named}
} }
type Background int type Background int
@@ -40,6 +42,7 @@ const (
inUnknown Background = iota inUnknown Background = iota
InGo InGo
InC InC
InPython
) )
// Type convert a Go/C type into raw type. // Type convert a Go/C type into raw type.
@@ -54,8 +57,11 @@ func (p Program) Type(typ types.Type, bg Background) Type {
// FuncDecl converts a Go/C function declaration into raw type. // FuncDecl converts a Go/C function declaration into raw type.
func (p Program) FuncDecl(sig *types.Signature, bg Background) Type { func (p Program) FuncDecl(sig *types.Signature, bg Background) Type {
recv := sig.Recv()
if bg == InGo { 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} return &aType{p.toLLVMFunc(sig), rawType{sig}, vkFuncDecl}
} }
@@ -115,10 +121,16 @@ func (p goTypes) cvtNamed(t *types.Named) (raw *types.Named, cvt bool) {
defer func() { defer func() {
p.typs[unsafe.Pointer(t)] = unsafe.Pointer(raw) p.typs[unsafe.Pointer(t)] = unsafe.Pointer(raw)
}() }()
id := t.String()
if named, ok := p.named[id]; ok {
return named, false
}
named := types.NewNamed(t.Obj(), types.Typ[types.Int], nil)
p.named[id] = named
defer delete(p.named, id)
if tund, cvt := p.cvtType(t.Underlying()); cvt { if tund, cvt := p.cvtType(t.Underlying()); cvt {
old := t.Obj() named.SetUnderlying(tund)
obj := types.NewTypeName(old.Pos(), old.Pkg(), old.Name(), nil) return named, true
return types.NewNamed(obj, tund, nil), true
} }
return t, false return t, false
} }

Binary file not shown.

View File

@@ -8,7 +8,7 @@ source_filename = "github.com/goplus/llgo/x/sqlite"
define ptr @"(*github.com/goplus/llgo/x/sqlite.Errno).Errstr"(ptr %0) { define ptr @"(*github.com/goplus/llgo/x/sqlite.Errno).Errstr"(ptr %0) {
_llgo_0: _llgo_0:
%1 = load i32, ptr %0, align 4 %1 = load i32, ptr %0, align 4
%2 = call ptr @sqlite3_errstr() %2 = call ptr @sqlite3_errstr(i32 %1)
ret ptr %2 ret ptr %2
} }
@@ -35,8 +35,8 @@ _llgo_0:
define { ptr, i32 } @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).Prepare"(ptr %0, %"github.com/goplus/llgo/internal/runtime.String" %1, ptr %2) { 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: _llgo_0:
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) %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) %4 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %1, 0
%5 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %1) %5 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %1, 1
%6 = trunc i64 %5 to i32 %6 = trunc i64 %5 to i32
%7 = call i32 @sqlite3_prepare(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 %8 = load ptr, ptr %3, align 8
@@ -48,8 +48,8 @@ _llgo_0:
define { ptr, i32 } @"(*github.com/goplus/llgo/x/sqlite.Sqlite3).PrepareV2"(ptr %0, %"github.com/goplus/llgo/internal/runtime.String" %1, ptr %2) { 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: _llgo_0:
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) %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) %4 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %1, 0
%5 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %1) %5 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %1, 1
%6 = trunc i64 %5 to i32 %6 = trunc i64 %5 to i32
%7 = call i32 @sqlite3_prepare_v2(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 %8 = load ptr, ptr %3, align 8
@@ -61,8 +61,8 @@ _llgo_0:
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) { 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: _llgo_0:
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) %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) %5 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %1, 0
%6 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %1) %6 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %1, 1
%7 = trunc i64 %6 to i32 %7 = trunc i64 %6 to i32
%8 = call i32 @sqlite3_prepare_v3(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 %9 = load ptr, ptr %4, align 8
@@ -84,7 +84,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void ret void
} }
declare ptr @sqlite3_errstr() declare ptr @sqlite3_errstr(i32)
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
@@ -92,12 +92,8 @@ declare i32 @sqlite3_open(ptr, ptr)
declare i32 @sqlite3_open_v2(ptr, ptr, i32, 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 i32 @sqlite3_prepare(ptr, ptr, i32, ptr, ptr)
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String") declare i32 @sqlite3_prepare_v2(ptr, ptr, i32, ptr, ptr)
declare i32 @sqlite3_prepare(ptr, i32, ptr, ptr) declare i32 @sqlite3_prepare_v3(ptr, ptr, i32, i32, ptr, ptr)
declare i32 @sqlite3_prepare_v2(ptr, i32, ptr, ptr)
declare i32 @sqlite3_prepare_v3(ptr, i32, i32, ptr, ptr)

View File

@@ -22,7 +22,7 @@ import (
"os/exec" "os/exec"
"strings" "strings"
"github.com/goplus/llgo/x/clang/ast" "github.com/goplus/llgo/xtool/clang/ast"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
) )

View File

@@ -22,7 +22,7 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"github.com/goplus/llgo/x/clang/pathutil" "github.com/goplus/llgo/xtool/clang/pathutil"
) )
const ( const (

View File

@@ -26,9 +26,9 @@ import (
"strconv" "strconv"
"github.com/goplus/gogen" "github.com/goplus/gogen"
"github.com/goplus/llgo/x/clang/types/scanner" "github.com/goplus/llgo/xtool/clang/types/scanner"
ctypes "github.com/goplus/llgo/x/clang/types" ctypes "github.com/goplus/llgo/xtool/clang/types"
) )
const ( const (

View File

@@ -19,7 +19,7 @@ package llvm
import ( import (
"os" "os"
"github.com/goplus/llgo/x/nm" "github.com/goplus/llgo/xtool/nm"
) )
type Env struct { type Env struct {