Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
695d3f3327 | ||
|
|
7979cfcb06 | ||
|
|
2986cb0c5f | ||
|
|
5a175955a9 | ||
|
|
cb507f43a7 | ||
|
|
8b148d72c2 | ||
|
|
616596e571 | ||
|
|
2849fe4841 | ||
|
|
98065e80d0 | ||
|
|
286b520d83 | ||
|
|
769b93a277 | ||
|
|
c30ed1b3c8 | ||
|
|
78d7f984d1 | ||
|
|
e88f7e6659 | ||
|
|
e8ff879943 | ||
|
|
f09d5bd155 | ||
|
|
53e73fc622 | ||
|
|
ba94d6f04e | ||
|
|
449f91ab14 | ||
|
|
70623dd554 | ||
|
|
35dc6dcd85 | ||
|
|
41dfafe957 | ||
|
|
2cc1bdee19 | ||
|
|
eb4146d80d | ||
|
|
0d68066086 | ||
|
|
7039cb3bc2 | ||
|
|
510f2f4769 | ||
|
|
5415f68c1b | ||
|
|
475f0fa2ff | ||
|
|
c58b1140d8 | ||
|
|
c97c1e97b9 | ||
|
|
ba3d82e5e5 | ||
|
|
d432899b42 | ||
|
|
08da38a609 | ||
|
|
6a3eb2f2f9 | ||
|
|
6a02c3ac4c | ||
|
|
f1761c0c9c | ||
|
|
39076c75cf | ||
|
|
3be8cacc24 | ||
|
|
46a9df47e4 | ||
|
|
d44a31cc62 | ||
|
|
00b2fd1479 | ||
|
|
5f08e7a612 | ||
|
|
5c8725373a | ||
|
|
c455f6e730 | ||
|
|
3cc83b8ec4 | ||
|
|
f74de76d70 | ||
|
|
ea8ddc6451 | ||
|
|
5f36c37cf2 | ||
|
|
2ad2873278 | ||
|
|
3855895808 | ||
|
|
f86cd74a98 | ||
|
|
1e6ecbadcd | ||
|
|
33716a3385 | ||
|
|
252f0bf967 | ||
|
|
2c3e1d1055 | ||
|
|
aafa639bf1 | ||
|
|
815a8a74fc | ||
|
|
840ea80e20 | ||
|
|
773ae2c8c6 | ||
|
|
4aadb4b86f | ||
|
|
91d1d71f6d | ||
|
|
a3d6a94600 | ||
|
|
43ae7a23b2 | ||
|
|
b6005886fa | ||
|
|
28dd34a136 | ||
|
|
3a3c263203 | ||
|
|
87b7ecd1d6 | ||
|
|
f5a309b5ad | ||
|
|
0f00add402 | ||
|
|
1014fa53dd | ||
|
|
04568835bd | ||
|
|
e4f8edc07c | ||
|
|
a8533d1677 | ||
|
|
edcb66afb7 |
8
.github/workflows/go.yml
vendored
8
.github/workflows/go.yml
vendored
@@ -60,4 +60,10 @@ jobs:
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
run: go test -v -coverprofile="coverage.txt" -covermode=atomic ./...
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: goplus/llgo
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,6 +8,10 @@
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
err.log
|
||||
|
||||
_go/
|
||||
_runtime/
|
||||
_tinygo/
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
|
||||
@@ -4,7 +4,8 @@ llgo - A Go compiler based on LLVM
|
||||
[](https://github.com/goplus/llgo/actions/workflows/go.yml)
|
||||
[](https://goreportcard.com/report/github.com/goplus/llgo)
|
||||
[](https://github.com/goplus/llgo/releases)
|
||||
[](https://pkg.go.dev/github.com/goplus/llgo)
|
||||
<!--
|
||||
[](https://codecov.io/gh/goplus/llgo)
|
||||
-->
|
||||
[](https://pkg.go.dev/github.com/goplus/llgo)
|
||||
[](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).
|
||||
|
||||
@@ -20,28 +20,35 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/internal/llgen"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "Usage: llgen xxx.go [pkgPath]")
|
||||
fmt.Fprintln(os.Stderr, "Usage: llgen <pkg> [pkgPath]")
|
||||
return
|
||||
}
|
||||
|
||||
inFile := os.Args[1]
|
||||
|
||||
dir, _ := filepath.Split(inFile)
|
||||
outFile := dir + "out.ll"
|
||||
|
||||
pkgPath := ""
|
||||
if len(os.Args) == 3 {
|
||||
pkgPath = os.Args[2]
|
||||
} else {
|
||||
pkgPath = llgen.PkgPath(dir)
|
||||
fname := "llgo_autogen.ll"
|
||||
if inCompilerDir(dir) {
|
||||
fname = "out.ll"
|
||||
}
|
||||
outFile := dir + fname
|
||||
|
||||
llgen.Init()
|
||||
llgen.Do(pkgPath, inFile, outFile)
|
||||
if len(os.Args) >= 3 {
|
||||
llgen.Do(os.Args[2], inFile, outFile)
|
||||
} else {
|
||||
llgen.DoFile(inFile, outFile)
|
||||
}
|
||||
}
|
||||
|
||||
func inCompilerDir(dir string) bool {
|
||||
dir, _ = filepath.Abs(dir)
|
||||
return strings.Contains(filepath.ToSlash(dir), "/llgo/cl/")
|
||||
}
|
||||
|
||||
16
cl/_testcgo/any/in.go
Normal file
16
cl/_testcgo/any/in.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
func incVal(a any) int {
|
||||
return a.(int) + 1
|
||||
}
|
||||
|
||||
//go:linkname printf C.printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
|
||||
|
||||
func main() {
|
||||
printf(&format[0], incVal(100))
|
||||
}
|
||||
55
cl/_testcgo/any/out.ll
Normal file
55
cl/_testcgo/any/out.ll
Normal file
@@ -0,0 +1,55 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
|
||||
|
||||
@main.format = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define i64 @main.incVal(%"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 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1)
|
||||
%3 = add i64 %2, 1
|
||||
ret i64 %3
|
||||
}
|
||||
|
||||
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
|
||||
store i8 72, ptr @main.format, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
|
||||
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
|
||||
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
|
||||
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
%0 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(i64 100)
|
||||
%1 = call i64 @main.incVal(%"github.com/goplus/llgo/internal/runtime.iface" %0)
|
||||
call void (ptr, ...) @printf(ptr @main.format, i64 %1)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
|
||||
declare i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface", ptr)
|
||||
|
||||
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)
|
||||
10
cl/_testcgo/hello/in.go
Normal file
10
cl/_testcgo/hello/in.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "github.com/goplus/llgo/cl/internal/libc"
|
||||
|
||||
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
|
||||
|
||||
func main() {
|
||||
sfmt := &format[0]
|
||||
libc.Printf(sfmt, libc.Strlen(sfmt))
|
||||
}
|
||||
43
cl/_testcgo/hello/out.ll
Normal file
43
cl/_testcgo/hello/out.ll
Normal file
@@ -0,0 +1,43 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@main.format = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
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/cl/internal/libc.init"()
|
||||
store i8 72, ptr @main.format, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
|
||||
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
|
||||
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
|
||||
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
%0 = call i32 @strlen(ptr @main.format)
|
||||
call void (ptr, ...) @"github.com/goplus/llgo/cl/internal/libc.Printf"(ptr @main.format, i32 %0)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/cl/internal/libc.init"()
|
||||
|
||||
declare i32 @strlen(ptr)
|
||||
|
||||
declare void @"github.com/goplus/llgo/cl/internal/libc.Printf"(ptr, ...)
|
||||
17
cl/_testcgo/strlen/in.go
Normal file
17
cl/_testcgo/strlen/in.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "C"
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname printf C.printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
//go:linkname strlen C.strlen
|
||||
func strlen(str *int8) C.int
|
||||
|
||||
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
|
||||
|
||||
func main() {
|
||||
sfmt := &format[0]
|
||||
printf(sfmt, strlen(sfmt))
|
||||
}
|
||||
40
cl/_testcgo/strlen/out.ll
Normal file
40
cl/_testcgo/strlen/out.ll
Normal file
@@ -0,0 +1,40 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@main.format = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
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
|
||||
store i8 72, ptr @main.format, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
|
||||
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
|
||||
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
|
||||
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
%0 = call i32 @strlen(ptr @main.format)
|
||||
call void (ptr, ...) @printf(ptr @main.format, i32 %0)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
|
||||
declare i32 @strlen(ptr)
|
||||
25
cl/_testcgo/struct/in.go
Normal file
25
cl/_testcgo/struct/in.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import "C"
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname printf C.printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
type Foo struct {
|
||||
A C.int
|
||||
ok bool
|
||||
}
|
||||
|
||||
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
|
||||
|
||||
func (p Foo) Print() {
|
||||
if p.ok {
|
||||
printf(&format[0], p.A)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
foo := Foo{100, true}
|
||||
foo.Print()
|
||||
}
|
||||
70
cl/_testcgo/struct/out.ll
Normal file
70
cl/_testcgo/struct/out.ll
Normal file
@@ -0,0 +1,70 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
%main.Foo = type { i32, i1 }
|
||||
|
||||
@main.format = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define void @"(main.Foo).Print"(%main.Foo %0) {
|
||||
_llgo_0:
|
||||
%1 = alloca %main.Foo, align 8
|
||||
store %main.Foo %0, ptr %1, align 4
|
||||
%2 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 1
|
||||
%3 = load i1, ptr %2, align 1
|
||||
br i1 %3, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
%4 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 0
|
||||
%5 = load i32, ptr %4, align 4
|
||||
call void (ptr, ...) @printf(ptr @main.format, i32 %5)
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"(*main.Foo).Print"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = load %main.Foo, ptr %0, align 4
|
||||
call void @"(main.Foo).Print"(%main.Foo %1)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
store i8 72, ptr @main.format, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
|
||||
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
|
||||
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
|
||||
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
%0 = alloca %main.Foo, align 8
|
||||
%1 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 0
|
||||
%2 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 1
|
||||
store i32 100, ptr %1, align 4
|
||||
store i1 true, ptr %2, align 1
|
||||
%3 = load %main.Foo, ptr %0, align 4
|
||||
call void @"(main.Foo).Print"(%main.Foo %3)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
25
cl/_testcgo/typalias/in.go
Normal file
25
cl/_testcgo/typalias/in.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import "C"
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname printf C.printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
type Foo = struct {
|
||||
A C.int
|
||||
ok bool
|
||||
}
|
||||
|
||||
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
|
||||
|
||||
func Print(p *Foo) {
|
||||
if p.ok {
|
||||
printf(&format[0], p.A)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
foo := &Foo{100, true}
|
||||
Print(foo)
|
||||
}
|
||||
60
cl/_testcgo/typalias/out.ll
Normal file
60
cl/_testcgo/typalias/out.ll
Normal file
@@ -0,0 +1,60 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@main.format = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define void @main.Print(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 1
|
||||
%2 = load i1, ptr %1, align 1
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
%3 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 0
|
||||
%4 = load i32, ptr %3, align 4
|
||||
call void (ptr, ...) @printf(ptr @main.format, i32 %4)
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
store i8 72, ptr @main.format, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
|
||||
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
|
||||
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
|
||||
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
|
||||
%1 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 0
|
||||
%2 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 1
|
||||
store i32 100, ptr %1, align 4
|
||||
store i1 true, ptr %2, align 1
|
||||
call void @main.Print(ptr %0)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64)
|
||||
@@ -3,6 +3,18 @@ source_filename = "apkg"
|
||||
|
||||
@"apkg.init$guard" = global ptr null
|
||||
|
||||
define double @apkg.Max(double %0, double %1) {
|
||||
_llgo_0:
|
||||
%2 = fcmp ogt double %0, %1
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret double %0
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret double %1
|
||||
}
|
||||
|
||||
define void @apkg.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"apkg.init$guard", align 1
|
||||
@@ -15,15 +27,3 @@ _llgo_1: ; preds = %_llgo_0
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define double @apkg.Max(double %0, double %1) {
|
||||
_llgo_0:
|
||||
%2 = fcmp ogt double %0, %1
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret double %0
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret double %1
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
%0 = call i64 @main.max(i64 1, i64 2)
|
||||
ret void
|
||||
}
|
||||
|
||||
define i64 @main.max(i64 %0, i64 %1) {
|
||||
_llgo_0:
|
||||
%2 = icmp sgt i64 %0, %1
|
||||
@@ -27,10 +34,3 @@ _llgo_1: ; preds = %_llgo_0
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret i64 %1
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
%0 = call i64 @main.max(i64 1, i64 2)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@main.hello = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
|
||||
19
cl/_testdata/method/in.go
Normal file
19
cl/_testdata/method/in.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
type T int
|
||||
|
||||
func (a T) Add(b T) T {
|
||||
return a + b
|
||||
}
|
||||
|
||||
//go:linkname printf C.printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
|
||||
|
||||
func main() {
|
||||
a := T(1)
|
||||
printf(&format[0], a.Add(2))
|
||||
}
|
||||
51
cl/_testdata/method/out.ll
Normal file
51
cl/_testdata/method/out.ll
Normal file
@@ -0,0 +1,51 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@main.format = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define i64 @"(main.T).Add"(i64 %0, i64 %1) {
|
||||
_llgo_0:
|
||||
%2 = add i64 %0, %1
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @"(*main.T).Add"(ptr %0, i64 %1) {
|
||||
_llgo_0:
|
||||
%2 = load i64, ptr %0, align 4
|
||||
%3 = call i64 @"(main.T).Add"(i64 %2, i64 %1)
|
||||
ret i64 %3
|
||||
}
|
||||
|
||||
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
|
||||
store i8 72, ptr @main.format, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
|
||||
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
|
||||
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
|
||||
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
%0 = call i64 @"(main.T).Add"(i64 1, i64 2)
|
||||
call void (ptr, ...) @printf(ptr @main.format, i64 %0)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname printf printf
|
||||
//go:linkname printf C.printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
var hello = [...]int8{'H', 'e', 'l', 'l', 'o', '\n', 0}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@main.hello = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
@@ -24,11 +24,11 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
call void (ptr, ...) @printf(ptr @main.hello)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
|
||||
12
cl/_testdata/printval/in.go
Normal file
12
cl/_testdata/printval/in.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname printf C.printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
|
||||
|
||||
func main() {
|
||||
printf(&format[0], 100)
|
||||
}
|
||||
37
cl/_testdata/printval/out.ll
Normal file
37
cl/_testdata/printval/out.ll
Normal file
@@ -0,0 +1,37 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@main.format = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
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
|
||||
store i8 72, ptr @main.format, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
|
||||
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
|
||||
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
|
||||
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
call void (ptr, ...) @printf(ptr @main.format, i64 100)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
19
cl/_testdata/ptrmthd/in.go
Normal file
19
cl/_testdata/ptrmthd/in.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname printf C.printf
|
||||
func printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
type T int8
|
||||
|
||||
func (f *T) Print(v int) {
|
||||
printf((*int8)(f), v)
|
||||
}
|
||||
|
||||
var format = [...]T{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
|
||||
|
||||
func main() {
|
||||
f := &format[0]
|
||||
f.Print(100)
|
||||
}
|
||||
43
cl/_testdata/ptrmthd/out.ll
Normal file
43
cl/_testdata/ptrmthd/out.ll
Normal file
@@ -0,0 +1,43 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@main.format = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define void @"(*main.T).Print"(ptr %0, i64 %1) {
|
||||
_llgo_0:
|
||||
call void (ptr, ...) @printf(ptr %0, i64 %1)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
store i8 72, ptr @main.format, align 1
|
||||
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
|
||||
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
|
||||
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
|
||||
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
|
||||
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
|
||||
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
|
||||
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
|
||||
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @main.init()
|
||||
call void @"(*main.T).Print"(ptr @main.format, i64 100)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @printf(ptr, ...)
|
||||
@@ -1,8 +1,8 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@main.a = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
|
||||
80
cl/builtin_test.go
Normal file
80
cl/builtin_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 cl
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"testing"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
func TestIsAny(t *testing.T) {
|
||||
if isAny(types.Typ[types.UntypedInt]) {
|
||||
t.Fatal("isAny?")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntVal(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal("intVal: no error?")
|
||||
}
|
||||
}()
|
||||
intVal(&ssa.Parameter{})
|
||||
}
|
||||
|
||||
func TestIgnoreName(t *testing.T) {
|
||||
if !ignoreName("runtime.foo") || !ignoreName("runtime/foo") || !ignoreName("internal/abi") {
|
||||
t.Fatal("ignoreName failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrImport(t *testing.T) {
|
||||
var ctx context
|
||||
pkg := types.NewPackage("foo", "foo")
|
||||
ctx.importPkg(pkg)
|
||||
}
|
||||
|
||||
func TestErrInitLinkname(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal("initLinkname: no error?")
|
||||
}
|
||||
}()
|
||||
var ctx context
|
||||
ctx.initLinkname("foo", "//go:linkname Printf printf")
|
||||
}
|
||||
|
||||
func TestErrVarOf(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal("varOf: no error?")
|
||||
}
|
||||
}()
|
||||
prog := llssa.NewProgram(nil)
|
||||
pkg := prog.NewPackage("foo", "foo")
|
||||
pkgTypes := types.NewPackage("foo", "foo")
|
||||
ctx := &context{
|
||||
pkg: pkg,
|
||||
goTyps: pkgTypes,
|
||||
}
|
||||
ssaPkg := &ssa.Package{Pkg: pkgTypes}
|
||||
g := &ssa.Global{Pkg: ssaPkg}
|
||||
ctx.varOf(g)
|
||||
}
|
||||
123
cl/cltest/cltest.go
Normal file
123
cl/cltest/cltest.go
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 cltest
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/goplus/gogen/packages"
|
||||
"github.com/goplus/llgo/cl"
|
||||
"github.com/goplus/llgo/internal/llgen"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cl.SetDebug(cl.DbgFlagAll)
|
||||
llssa.Initialize(llssa.InitAll)
|
||||
llssa.SetDebug(llssa.DbgFlagAll)
|
||||
}
|
||||
|
||||
func FromDir(t *testing.T, sel, relDir string, byLLGen bool) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal("Getwd failed:", err)
|
||||
}
|
||||
dir = path.Join(dir, relDir)
|
||||
fis, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
t.Fatal("ReadDir failed:", err)
|
||||
}
|
||||
for _, fi := range fis {
|
||||
name := fi.Name()
|
||||
if !fi.IsDir() || strings.HasPrefix(name, "_") {
|
||||
continue
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
testFrom(t, dir+"/"+name, sel, byLLGen)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Pkg(t *testing.T, pkgPath, outFile string) {
|
||||
b, err := os.ReadFile(outFile)
|
||||
if err != nil {
|
||||
t.Fatal("ReadFile failed:", err)
|
||||
}
|
||||
expected := string(b)
|
||||
if v := llgen.GenFrom(pkgPath); v != expected {
|
||||
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func testFrom(t *testing.T, pkgDir, sel string, byLLGen bool) {
|
||||
if sel != "" && !strings.Contains(pkgDir, sel) {
|
||||
return
|
||||
}
|
||||
log.Println("Parsing", pkgDir)
|
||||
in := pkgDir + "/in.go"
|
||||
out := pkgDir + "/out.ll"
|
||||
b, err := os.ReadFile(out)
|
||||
if err != nil {
|
||||
t.Fatal("ReadFile failed:", err)
|
||||
}
|
||||
expected := string(b)
|
||||
if byLLGen {
|
||||
if v := llgen.GenFrom(in); v != expected {
|
||||
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
|
||||
}
|
||||
} else {
|
||||
TestCompileEx(t, nil, in, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompileEx(t *testing.T, src any, fname, expected string) {
|
||||
t.Helper()
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, fname, src, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal("ParseFile failed:", err)
|
||||
}
|
||||
files := []*ast.File{f}
|
||||
name := f.Name.Name
|
||||
pkg := types.NewPackage(name, name)
|
||||
imp := packages.NewImporter(fset)
|
||||
foo, _, err := ssautil.BuildPackage(
|
||||
&types.Config{Importer: imp}, fset, pkg, files, ssa.SanityCheckFunctions)
|
||||
if err != nil {
|
||||
t.Fatal("BuildPackage failed:", err)
|
||||
}
|
||||
foo.WriteTo(os.Stderr)
|
||||
prog := llssa.NewProgram(nil)
|
||||
ret, err := cl.NewPackage(prog, foo, files)
|
||||
if err != nil {
|
||||
t.Fatal("cl.NewPackage failed:", err)
|
||||
}
|
||||
if v := ret.String(); v != expected {
|
||||
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
|
||||
}
|
||||
}
|
||||
258
cl/compile.go
258
cl/compile.go
@@ -19,11 +19,13 @@ package cl
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
@@ -56,7 +58,7 @@ func SetDebug(dbgFlags dbgFlags) {
|
||||
const (
|
||||
fnNormal = iota
|
||||
fnHasVArg
|
||||
fnUnsafeInit
|
||||
fnIgnore
|
||||
)
|
||||
|
||||
func funcKind(vfn ssa.Value) int {
|
||||
@@ -64,8 +66,8 @@ func funcKind(vfn ssa.Value) int {
|
||||
params := fn.Signature.Params()
|
||||
n := params.Len()
|
||||
if n == 0 {
|
||||
if fn.Name() == "init" && fn.Pkg.Pkg.Path() == "unsafe" {
|
||||
return fnUnsafeInit
|
||||
if fn.Name() == "init" && ignorePkgInit(fn.Pkg.Pkg.Path()) {
|
||||
return fnIgnore
|
||||
}
|
||||
} else {
|
||||
last := params.At(n - 1)
|
||||
@@ -77,11 +79,43 @@ func funcKind(vfn ssa.Value) int {
|
||||
return fnNormal
|
||||
}
|
||||
|
||||
func ignorePkgInit(pkgPath string) bool {
|
||||
switch pkgPath {
|
||||
case "unsafe", "syscall", "runtime/cgo":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ignoreName(name string) bool {
|
||||
/* TODO(xsw): confirm this is not needed more
|
||||
if name == "unsafe.init" {
|
||||
return true
|
||||
}
|
||||
*/
|
||||
if strings.HasPrefix(name, "internal/") || strings.HasPrefix(name, "crypto/") ||
|
||||
strings.HasPrefix(name, "arena.") || strings.HasPrefix(name, "maps.") ||
|
||||
strings.HasPrefix(name, "time.") || strings.HasPrefix(name, "syscall.") ||
|
||||
strings.HasPrefix(name, "os.") || strings.HasPrefix(name, "plugin.") ||
|
||||
strings.HasPrefix(name, "reflect.") || strings.HasPrefix(name, "errors.") {
|
||||
return true // TODO(xsw)
|
||||
}
|
||||
return inPkg(name, "runtime") || inPkg(name, "sync")
|
||||
}
|
||||
|
||||
func inPkg(name, pkg string) bool {
|
||||
if len(name) > len(pkg) && strings.HasPrefix(name, pkg) {
|
||||
c := name[len(pkg)]
|
||||
return c == '.' || c == '/'
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type none = struct{}
|
||||
|
||||
type instrAndValue interface {
|
||||
type instrOrValue interface {
|
||||
ssa.Instruction
|
||||
ssa.Value
|
||||
}
|
||||
@@ -91,22 +125,52 @@ type context struct {
|
||||
pkg llssa.Package
|
||||
fn llssa.Function
|
||||
fset *token.FileSet
|
||||
goProg *ssa.Program
|
||||
goTyps *types.Package
|
||||
goPkg *ssa.Package
|
||||
link map[string]string // pkgPath.nameInPkg => linkname
|
||||
loaded map[*types.Package]none // loaded packages
|
||||
bvals map[ssa.Value]llssa.Expr // block values
|
||||
link map[string]string // pkgPath.nameInPkg => linkname
|
||||
loaded map[*types.Package]none // loaded packages
|
||||
bvals map[ssa.Value]llssa.Expr // block values
|
||||
vargs map[*ssa.Alloc][]llssa.Expr // varargs
|
||||
inits []func()
|
||||
}
|
||||
|
||||
func (p *context) compileType(pkg llssa.Package, member *ssa.Type) {
|
||||
panic("todo")
|
||||
func (p *context) compileType(pkg llssa.Package, t *ssa.Type) {
|
||||
tn := t.Object().(*types.TypeName)
|
||||
if tn.IsAlias() { // don't need to compile alias type
|
||||
return
|
||||
}
|
||||
tnName := tn.Name()
|
||||
typ := tn.Type()
|
||||
name := llssa.FullName(tn.Pkg(), tnName)
|
||||
if ignoreName(name) {
|
||||
return
|
||||
}
|
||||
if debugInstr {
|
||||
log.Println("==> NewType", name, typ)
|
||||
}
|
||||
p.compileMethods(pkg, typ)
|
||||
p.compileMethods(pkg, types.NewPointer(typ))
|
||||
}
|
||||
|
||||
func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
|
||||
prog := p.goProg
|
||||
mthds := prog.MethodSets.MethodSet(typ)
|
||||
for i, n := 0, mthds.Len(); i < n; i++ {
|
||||
mthd := mthds.At(i)
|
||||
if ssaMthd := prog.MethodValue(mthd); ssaMthd != nil {
|
||||
p.compileFunc(pkg, mthd.Obj().Pkg(), ssaMthd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Global variable.
|
||||
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||
typ := gbl.Type()
|
||||
name := fullName(gbl.Pkg.Pkg, gbl.Name())
|
||||
name := llssa.FullName(gbl.Pkg.Pkg, gbl.Name())
|
||||
if ignoreName(name) || checkCgo(gbl.Name()) {
|
||||
return
|
||||
}
|
||||
if debugInstr {
|
||||
log.Println("==> NewVar", name, typ)
|
||||
}
|
||||
@@ -114,15 +178,16 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||
g.Init(p.prog.Null(g.Type))
|
||||
}
|
||||
|
||||
func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) {
|
||||
name := p.funcName(f.Pkg.Pkg, f)
|
||||
if name == "unsafe.init" {
|
||||
func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) {
|
||||
sig := f.Signature
|
||||
name, ok := p.funcName(pkgTypes, f, true)
|
||||
if !ok { // ignored
|
||||
return
|
||||
}
|
||||
if debugInstr {
|
||||
log.Println("==> NewFunc", name)
|
||||
log.Println("==> NewFunc", name, "type:", sig.Recv(), sig)
|
||||
}
|
||||
fn := pkg.NewFunc(name, f.Signature)
|
||||
fn := pkg.NewFunc(name, sig)
|
||||
p.inits = append(p.inits, func() {
|
||||
p.fn = fn
|
||||
defer func() {
|
||||
@@ -140,6 +205,7 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) {
|
||||
}
|
||||
fn.MakeBlocks(nblk)
|
||||
b := fn.NewBuilder()
|
||||
p.bvals = make(map[ssa.Value]llssa.Expr)
|
||||
for i, block := range f.Blocks {
|
||||
p.compileBlock(b, block, i == 0 && name == "main")
|
||||
}
|
||||
@@ -149,7 +215,6 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) {
|
||||
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bool) llssa.BasicBlock {
|
||||
ret := p.fn.Block(block.Index)
|
||||
b.SetBlock(ret)
|
||||
p.bvals = make(map[ssa.Value]llssa.Expr)
|
||||
if doInit {
|
||||
fn := p.pkg.FuncOf("main.init")
|
||||
b.Call(fn.Expr)
|
||||
@@ -160,23 +225,74 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bo
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret llssa.Expr) {
|
||||
if v, ok := p.bvals[iv]; ok {
|
||||
return v
|
||||
func isAny(t types.Type) bool {
|
||||
if t, ok := t.(*types.Interface); ok {
|
||||
return t.Empty()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func intVal(v ssa.Value) int64 {
|
||||
if c, ok := v.(*ssa.Const); ok {
|
||||
if iv, exact := constant.Int64Val(c.Value); exact {
|
||||
return iv
|
||||
}
|
||||
}
|
||||
panic("intVal: ssa.Value is not a const int")
|
||||
}
|
||||
|
||||
func (p *context) isVArgs(vx ssa.Value) (ret []llssa.Expr, ok bool) {
|
||||
if va, vok := vx.(*ssa.Alloc); vok {
|
||||
ret, ok = p.vargs[va] // varargs: this is a varargs index
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool {
|
||||
if v.Comment == "varargs" { // this is a varargs allocation
|
||||
if arr, ok := t.Elem().(*types.Array); ok {
|
||||
if isAny(arr.Elem()) {
|
||||
p.vargs[v] = make([]llssa.Expr, arr.Len())
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) {
|
||||
if asValue {
|
||||
if v, ok := p.bvals[iv]; ok {
|
||||
return v
|
||||
}
|
||||
log.Panicln("unreachable:", iv)
|
||||
}
|
||||
switch v := iv.(type) {
|
||||
case *ssa.Call:
|
||||
call := v.Call
|
||||
kind := funcKind(call.Value)
|
||||
if kind == fnUnsafeInit {
|
||||
cv := call.Value
|
||||
kind := funcKind(cv)
|
||||
if kind == fnIgnore {
|
||||
return
|
||||
}
|
||||
if debugGoSSA {
|
||||
log.Println(">>> Call", call.Value, call.Args)
|
||||
log.Println(">>> Call", cv, call.Args)
|
||||
}
|
||||
if builtin, ok := cv.(*ssa.Builtin); ok {
|
||||
fn := builtin.Name()
|
||||
if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr
|
||||
arg := call.Args[0]
|
||||
ret = p.compileValue(b, arg)
|
||||
// log.Println("wrapnilchk:", ret.TypeOf())
|
||||
} else {
|
||||
args := p.compileValues(b, call.Args, kind)
|
||||
ret = b.BuiltinCall(fn, args...)
|
||||
}
|
||||
} else {
|
||||
fn := p.compileValue(b, cv)
|
||||
args := p.compileValues(b, call.Args, kind)
|
||||
ret = b.Call(fn, args...)
|
||||
}
|
||||
fn := p.compileValue(b, call.Value)
|
||||
args := p.compileValues(b, call.Args, kind)
|
||||
ret = b.Call(fn, args...)
|
||||
case *ssa.BinOp:
|
||||
x := p.compileValue(b, v.X)
|
||||
y := p.compileValue(b, v.Y)
|
||||
@@ -184,13 +300,47 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l
|
||||
case *ssa.UnOp:
|
||||
x := p.compileValue(b, v.X)
|
||||
ret = b.UnOp(v.Op, x)
|
||||
case *ssa.IndexAddr:
|
||||
case *ssa.ChangeType:
|
||||
t := v.Type()
|
||||
x := p.compileValue(b, v.X)
|
||||
ret = b.ChangeType(p.prog.Type(t), x)
|
||||
case *ssa.Convert:
|
||||
t := v.Type()
|
||||
x := p.compileValue(b, v.X)
|
||||
ret = b.Convert(p.prog.Type(t), x)
|
||||
case *ssa.FieldAddr:
|
||||
x := p.compileValue(b, v.X)
|
||||
ret = b.FieldAddr(x, v.Field)
|
||||
case *ssa.IndexAddr:
|
||||
vx := v.X
|
||||
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index
|
||||
return
|
||||
}
|
||||
x := p.compileValue(b, vx)
|
||||
idx := p.compileValue(b, v.Index)
|
||||
ret = b.IndexAddr(x, idx)
|
||||
case *ssa.Slice:
|
||||
vx := v.X
|
||||
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs slice
|
||||
return
|
||||
}
|
||||
panic("todo")
|
||||
case *ssa.Alloc:
|
||||
t := v.Type().(*types.Pointer)
|
||||
if p.checkVArgs(v, t) { // varargs: this is a varargs allocation
|
||||
return
|
||||
}
|
||||
ret = b.Alloc(t, v.Heap)
|
||||
case *ssa.MakeInterface:
|
||||
const (
|
||||
delayExpr = true // varargs: don't need to convert an expr to any
|
||||
)
|
||||
t := v.Type()
|
||||
ret = b.Alloc(p.prog.Type(t), v.Heap)
|
||||
x := p.compileValue(b, v.X)
|
||||
ret = b.MakeInterface(t, x, delayExpr)
|
||||
case *ssa.TypeAssert:
|
||||
x := p.compileValue(b, v.X)
|
||||
ret = b.TypeAssert(x, p.prog.Type(v.AssertedType), v.CommaOk)
|
||||
default:
|
||||
panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv))
|
||||
}
|
||||
@@ -199,13 +349,25 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l
|
||||
}
|
||||
|
||||
func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
|
||||
if iv, ok := instr.(instrAndValue); ok {
|
||||
p.compileInstrAndValue(b, iv)
|
||||
if iv, ok := instr.(instrOrValue); ok {
|
||||
p.compileInstrOrValue(b, iv, false)
|
||||
return
|
||||
}
|
||||
switch v := instr.(type) {
|
||||
case *ssa.Store:
|
||||
ptr := p.compileValue(b, v.Addr)
|
||||
va := v.Addr
|
||||
if va, ok := va.(*ssa.IndexAddr); ok {
|
||||
if args, ok := p.isVArgs(va.X); ok { // varargs: this is a varargs store
|
||||
idx := intVal(va.Index)
|
||||
val := v.Val
|
||||
if vi, ok := val.(*ssa.MakeInterface); ok {
|
||||
val = vi.X
|
||||
}
|
||||
args[idx] = p.compileValue(b, val)
|
||||
return
|
||||
}
|
||||
}
|
||||
ptr := p.compileValue(b, va)
|
||||
val := p.compileValue(b, v.Val)
|
||||
b.Store(ptr, val)
|
||||
case *ssa.Jump:
|
||||
@@ -229,14 +391,17 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
|
||||
thenb := fn.Block(succs[0].Index)
|
||||
elseb := fn.Block(succs[1].Index)
|
||||
b.If(cond, thenb, elseb)
|
||||
case *ssa.Panic:
|
||||
arg := p.compileValue(b, v.X).Do()
|
||||
b.Panic(arg)
|
||||
default:
|
||||
panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
|
||||
if iv, ok := v.(instrAndValue); ok {
|
||||
return p.compileInstrAndValue(b, iv)
|
||||
if iv, ok := v.(instrOrValue); ok {
|
||||
return p.compileInstrOrValue(b, iv, true)
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case *ssa.Parameter:
|
||||
@@ -262,19 +427,23 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
|
||||
func (p *context) compileVArg(ret []llssa.Expr, b llssa.Builder, v ssa.Value) []llssa.Expr {
|
||||
_ = b
|
||||
switch v := v.(type) {
|
||||
case *ssa.Slice: // varargs: this is a varargs slice
|
||||
if args, ok := p.isVArgs(v.X); ok {
|
||||
return append(ret, args...)
|
||||
}
|
||||
case *ssa.Const:
|
||||
if v.Value == nil {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
panic("todo")
|
||||
panic(fmt.Sprintf("compileVArg: unknown value - %T\n", v))
|
||||
}
|
||||
|
||||
func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int) []llssa.Expr {
|
||||
n := len(vals) - hasVArg
|
||||
ret := make([]llssa.Expr, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ret[i] = p.compileValue(b, vals[i])
|
||||
ret[i] = p.compileValue(b, vals[i]).Do()
|
||||
}
|
||||
if hasVArg > 0 {
|
||||
ret = p.compileVArg(ret, b, vals[n])
|
||||
@@ -291,31 +460,32 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
|
||||
val ssa.Member
|
||||
}
|
||||
|
||||
// Sort by position, so that the order of the functions in the IR matches
|
||||
// the order of functions in the source file. This is useful for testing,
|
||||
// for example.
|
||||
var members []*namedMember
|
||||
members := make([]*namedMember, 0, len(pkg.Members))
|
||||
for name, v := range pkg.Members {
|
||||
members = append(members, &namedMember{name, v})
|
||||
}
|
||||
sort.Slice(members, func(i, j int) bool {
|
||||
iPos := members[i].val.Pos()
|
||||
jPos := members[j].val.Pos()
|
||||
return iPos < jPos
|
||||
return members[i].name < members[j].name
|
||||
})
|
||||
|
||||
pkgProg := pkg.Prog
|
||||
pkgTypes := pkg.Pkg
|
||||
pkgName, pkgPath := pkgTypes.Name(), pkgTypes.Path()
|
||||
pkgName, pkgPath := pkgTypes.Name(), llssa.PathOf(pkgTypes)
|
||||
if pkgPath == llssa.PkgRuntime {
|
||||
prog.SetRuntime(pkgTypes)
|
||||
}
|
||||
ret = prog.NewPackage(pkgName, pkgPath)
|
||||
|
||||
ctx := &context{
|
||||
prog: prog,
|
||||
pkg: ret,
|
||||
fset: pkg.Prog.Fset,
|
||||
fset: pkgProg.Fset,
|
||||
goProg: pkgProg,
|
||||
goTyps: pkgTypes,
|
||||
goPkg: pkg,
|
||||
link: make(map[string]string),
|
||||
loaded: make(map[*types.Package]none),
|
||||
vargs: make(map[*ssa.Alloc][]llssa.Expr),
|
||||
}
|
||||
ctx.initFiles(pkgPath, files)
|
||||
for _, m := range members {
|
||||
@@ -326,7 +496,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
|
||||
// Do not try to build generic (non-instantiated) functions.
|
||||
continue
|
||||
}
|
||||
ctx.compileFunc(ret, member)
|
||||
ctx.compileFunc(ret, member.Pkg.Pkg, member)
|
||||
case *ssa.Type:
|
||||
ctx.compileType(ret, member)
|
||||
case *ssa.Global:
|
||||
|
||||
@@ -14,101 +14,30 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cl
|
||||
package cl_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/goplus/gogen/packages"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
"github.com/goplus/llgo/cl/cltest"
|
||||
"github.com/goplus/llgo/ssa"
|
||||
)
|
||||
|
||||
func TestFromTestdata(t *testing.T) {
|
||||
testFromDir(t, "", "./_testdata")
|
||||
}
|
||||
|
||||
func init() {
|
||||
SetDebug(DbgFlagAll)
|
||||
llssa.Initialize(llssa.InitAll)
|
||||
llssa.SetDebug(llssa.DbgFlagAll)
|
||||
}
|
||||
|
||||
func testFromDir(t *testing.T, sel, relDir string) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal("Getwd failed:", err)
|
||||
}
|
||||
dir = path.Join(dir, relDir)
|
||||
fis, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
t.Fatal("ReadDir failed:", err)
|
||||
}
|
||||
for _, fi := range fis {
|
||||
name := fi.Name()
|
||||
if !fi.IsDir() || strings.HasPrefix(name, "_") {
|
||||
continue
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
testFrom(t, dir+"/"+name, sel)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testFrom(t *testing.T, pkgDir, sel string) {
|
||||
if sel != "" && !strings.Contains(pkgDir, sel) {
|
||||
return
|
||||
}
|
||||
log.Println("Parsing", pkgDir)
|
||||
in := pkgDir + "/in.go"
|
||||
out := pkgDir + "/out.ll"
|
||||
expected, err := os.ReadFile(out)
|
||||
if err != nil {
|
||||
t.Fatal("ReadFile failed:", err)
|
||||
}
|
||||
testCompileEx(t, nil, in, string(expected))
|
||||
}
|
||||
|
||||
func testCompileEx(t *testing.T, src any, fname, expected string) {
|
||||
t.Helper()
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, fname, src, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal("ParseFile failed:", err)
|
||||
}
|
||||
files := []*ast.File{f}
|
||||
name := f.Name.Name
|
||||
pkg := types.NewPackage(name, name)
|
||||
imp := packages.NewImporter(fset)
|
||||
foo, _, err := ssautil.BuildPackage(
|
||||
&types.Config{Importer: imp}, fset, pkg, files, ssa.SanityCheckFunctions)
|
||||
if err != nil {
|
||||
t.Fatal("BuildPackage failed:", err)
|
||||
}
|
||||
foo.WriteTo(os.Stderr)
|
||||
prog := llssa.NewProgram(nil)
|
||||
ret, err := NewPackage(prog, foo, files)
|
||||
if err != nil {
|
||||
t.Fatal("cl.NewPackage failed:", err)
|
||||
}
|
||||
if v := ret.String(); v != expected {
|
||||
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func testCompile(t *testing.T, src, expected string) {
|
||||
t.Helper()
|
||||
testCompileEx(t, src, "foo.go", expected)
|
||||
cltest.TestCompileEx(t, src, "foo.go", expected)
|
||||
}
|
||||
|
||||
func TestFromTestcgo(t *testing.T) {
|
||||
cltest.FromDir(t, "", "./_testcgo", true)
|
||||
}
|
||||
|
||||
func TestFromTestdata(t *testing.T) {
|
||||
cltest.FromDir(t, "", "./_testdata", false)
|
||||
}
|
||||
|
||||
func TestRuntime(t *testing.T) {
|
||||
cltest.Pkg(t, ssa.PkgRuntime, "../internal/runtime/llgo_autogen.ll")
|
||||
}
|
||||
|
||||
func TestVar(t *testing.T) {
|
||||
@@ -118,8 +47,8 @@ var a int
|
||||
`, `; ModuleID = 'foo'
|
||||
source_filename = "foo"
|
||||
|
||||
@"foo.init$guard" = global ptr null
|
||||
@foo.a = global ptr null
|
||||
@"foo.init$guard" = global ptr null
|
||||
|
||||
define void @foo.init() {
|
||||
_llgo_0:
|
||||
@@ -147,6 +76,11 @@ source_filename = "foo"
|
||||
|
||||
@"foo.init$guard" = global ptr null
|
||||
|
||||
define i64 @foo.fn(i64 %0, double %1) {
|
||||
_llgo_0:
|
||||
ret i64 1
|
||||
}
|
||||
|
||||
define void @foo.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"foo.init$guard", align 1
|
||||
@@ -159,10 +93,5 @@ _llgo_1: ; preds = %_llgo_0
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define i64 @foo.fn(i64 %0, double %1) {
|
||||
_llgo_0:
|
||||
ret i64 1
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
54
cl/import.go
54
cl/import.go
@@ -51,7 +51,7 @@ func (p *context) importPkg(pkg *types.Package) {
|
||||
fset := p.fset
|
||||
names := scope.Names()
|
||||
contents := make(contentMap)
|
||||
pkgPath := pkg.Path()
|
||||
pkgPath := llssa.PathOf(pkg)
|
||||
for _, name := range names {
|
||||
if token.IsExported(name) {
|
||||
obj := scope.Lookup(name)
|
||||
@@ -98,41 +98,59 @@ func (p *context) initLinkname(pkgPath, line string) {
|
||||
if strings.HasPrefix(line, linkname) {
|
||||
text := strings.TrimSpace(line[len(linkname):])
|
||||
if idx := strings.IndexByte(text, ' '); idx > 0 {
|
||||
name := pkgPath + "." + text[:idx]
|
||||
link := strings.TrimLeft(text[idx+1:], " ")
|
||||
p.link[name] = link
|
||||
if strings.Contains(link, ".") { // eg. C.printf, C.strlen
|
||||
name := pkgPath + "." + text[:idx]
|
||||
p.link[name] = link[2:]
|
||||
} else {
|
||||
panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fullName(pkg *types.Package, name string) string {
|
||||
pkgPath := pkg.Name()
|
||||
if pkgPath != "main" {
|
||||
pkgPath = pkg.Path()
|
||||
}
|
||||
return pkgPath + "." + name
|
||||
}
|
||||
|
||||
// func: pkg.name
|
||||
// method: (pkg.T).name, (*pkg.T).name
|
||||
func funcName(pkg *types.Package, fn *ssa.Function) string {
|
||||
ret := fullName(pkg, fn.Name())
|
||||
sig := fn.Signature
|
||||
name := fn.Name()
|
||||
if recv := sig.Recv(); recv != nil {
|
||||
var tName string
|
||||
t := recv.Type()
|
||||
if tp, ok := t.(*types.Pointer); ok {
|
||||
t, tName = tp.Elem(), "*"
|
||||
}
|
||||
tName += llssa.NameOf(t.(*types.Named))
|
||||
return "(" + tName + ")." + name
|
||||
}
|
||||
ret := llssa.FullName(pkg, name)
|
||||
if ret == "main.main" {
|
||||
ret = "main"
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *context) funcName(pkg *types.Package, fn *ssa.Function) string {
|
||||
func checkCgo(fnName string) bool {
|
||||
return len(fnName) > 4 && fnName[0] == '_' && fnName[2] == 'g' && fnName[3] == 'o' &&
|
||||
(fnName[1] == 'C' || fnName[1] == 'c') &&
|
||||
(fnName[4] == '_' || strings.HasPrefix(fnName[4:], "Check"))
|
||||
}
|
||||
|
||||
func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, bool) {
|
||||
name := funcName(pkg, fn)
|
||||
if v, ok := p.link[name]; ok {
|
||||
return v
|
||||
if ignore && ignoreName(name) || checkCgo(fn.Name()) {
|
||||
return name, false
|
||||
}
|
||||
return name
|
||||
if v, ok := p.link[name]; ok {
|
||||
return v, true
|
||||
}
|
||||
return name, true
|
||||
}
|
||||
|
||||
func (p *context) funcOf(fn *ssa.Function) llssa.Function {
|
||||
pkgTypes := p.ensureLoaded(fn.Pkg.Pkg)
|
||||
pkg := p.pkg
|
||||
name := p.funcName(pkgTypes, fn)
|
||||
name, _ := p.funcName(pkgTypes, fn, false)
|
||||
if ret := pkg.FuncOf(name); ret != nil {
|
||||
return ret
|
||||
}
|
||||
@@ -142,7 +160,7 @@ func (p *context) funcOf(fn *ssa.Function) llssa.Function {
|
||||
func (p *context) varOf(v *ssa.Global) llssa.Global {
|
||||
pkgTypes := p.ensureLoaded(v.Pkg.Pkg)
|
||||
pkg := p.pkg
|
||||
name := fullName(pkgTypes, v.Name())
|
||||
name := llssa.FullName(pkgTypes, v.Name())
|
||||
if ret := pkg.VarOf(name); ret != nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
14
cl/internal/libc/libc.go
Normal file
14
cl/internal/libc/libc.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package libc
|
||||
|
||||
import "C"
|
||||
import _ "unsafe"
|
||||
|
||||
const (
|
||||
LLGoPackage = true
|
||||
)
|
||||
|
||||
//go:linkname Printf C.printf
|
||||
func Printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
//go:linkname Strlen C.strlen
|
||||
func Strlen(str *int8) C.int
|
||||
@@ -6,7 +6,7 @@ const (
|
||||
LLGoPackage = true
|
||||
)
|
||||
|
||||
//go:linkname Printf printf
|
||||
//go:linkname Printf C.printf
|
||||
func Printf(format *int8, __llgo_va_list ...any)
|
||||
|
||||
func Max(a, b int) int {
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 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 cl
|
||||
|
||||
/*
|
||||
// Define unimplemented intrinsic functions.
|
||||
//
|
||||
// Some functions are either normally implemented in Go assembly (like
|
||||
// sync/atomic functions) or intentionally left undefined to be implemented
|
||||
// directly in the compiler (like runtime/volatile functions). Either way, look
|
||||
// for these and implement them if this is the case.
|
||||
func (b *builder) defineIntrinsicFunction() {
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
var mathToLLVMMapping = map[string]string{
|
||||
"math.Ceil": "llvm.ceil.f64",
|
||||
"math.Exp": "llvm.exp.f64",
|
||||
"math.Exp2": "llvm.exp2.f64",
|
||||
"math.Floor": "llvm.floor.f64",
|
||||
"math.Log": "llvm.log.f64",
|
||||
"math.Sqrt": "llvm.sqrt.f64",
|
||||
"math.Trunc": "llvm.trunc.f64",
|
||||
}
|
||||
|
||||
// defineMathOp defines a math function body as a call to a LLVM intrinsic,
|
||||
// instead of the regular Go implementation. This allows LLVM to reason about
|
||||
// the math operation and (depending on the architecture) allows it to lower the
|
||||
// operation to very fast floating point instructions. If this is not possible,
|
||||
// LLVM will emit a call to a libm function that implements the same operation.
|
||||
//
|
||||
// One example of an optimization that LLVM can do is to convert
|
||||
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
|
||||
// beneficial on architectures where 64-bit floating point operations are (much)
|
||||
// more expensive than 32-bit ones.
|
||||
func (b *builder) defineMathOp() {
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
// Implement most math/bits functions.
|
||||
//
|
||||
// This implements all the functions that operate on bits. It does not yet
|
||||
// implement the arithmetic functions (like bits.Add), which also have LLVM
|
||||
// intrinsics.
|
||||
func (b *builder) defineMathBitsIntrinsic() bool {
|
||||
panic("todo")
|
||||
}
|
||||
*/
|
||||
@@ -33,8 +33,13 @@ func init() {
|
||||
}
|
||||
|
||||
func runCmd(cmd *base.Command, args []string) {
|
||||
build.Do(args, &build.Config{
|
||||
conf := &build.Config{
|
||||
Mode: build.ModeBuild,
|
||||
AppExt: build.DefaultAppExt(),
|
||||
})
|
||||
}
|
||||
if len(args) >= 2 && args[0] == "-o" {
|
||||
conf.OutFile = args[1]
|
||||
args = args[2:]
|
||||
}
|
||||
build.Do(args, conf)
|
||||
}
|
||||
|
||||
38
cmd/internal/clean/clean.go
Normal file
38
cmd/internal/clean/clean.go
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 clean implements the "llgo clean" command.
|
||||
package clean
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/cmd/internal/base"
|
||||
"github.com/goplus/llgo/internal/build"
|
||||
)
|
||||
|
||||
// llgo build
|
||||
var Cmd = &base.Command{
|
||||
UsageLine: "llgo clean [clean flags] [build flags] [packages]",
|
||||
Short: "Remove object files and cached files",
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.Run = runCmd
|
||||
}
|
||||
|
||||
func runCmd(cmd *base.Command, args []string) {
|
||||
conf := build.NewDefaultConf(0)
|
||||
build.Clean(args, conf)
|
||||
}
|
||||
@@ -20,7 +20,6 @@ package run
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/cmd/internal/base"
|
||||
"github.com/goplus/llgo/internal/build"
|
||||
@@ -49,7 +48,7 @@ func runCmd(cmd *base.Command, args []string) {
|
||||
}
|
||||
|
||||
func parseRunArgs(args []string) ([]string, []string, error) {
|
||||
n := parseArgs(args)
|
||||
n := build.SkipFlagArgs(args)
|
||||
if n < 0 {
|
||||
return nil, nil, errNoProj
|
||||
}
|
||||
@@ -65,15 +64,6 @@ func parseRunArgs(args []string) ([]string, []string, error) {
|
||||
return args[:n+1], args[n+1:], nil
|
||||
}
|
||||
|
||||
func parseArgs(args []string) int {
|
||||
for i, arg := range args {
|
||||
if !strings.HasPrefix(arg, "-") {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func isGoFile(fname string) bool {
|
||||
return filepath.Ext(fname) == ".go"
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
"github.com/goplus/llgo/cmd/internal/base"
|
||||
"github.com/goplus/llgo/cmd/internal/build"
|
||||
"github.com/goplus/llgo/cmd/internal/clean"
|
||||
"github.com/goplus/llgo/cmd/internal/help"
|
||||
"github.com/goplus/llgo/cmd/internal/install"
|
||||
"github.com/goplus/llgo/cmd/internal/run"
|
||||
@@ -42,6 +43,7 @@ func init() {
|
||||
build.Cmd,
|
||||
install.Cmd,
|
||||
run.Cmd,
|
||||
clean.Cmd,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
go.mod
2
go.mod
@@ -5,7 +5,7 @@ go 1.18
|
||||
require (
|
||||
github.com/aykevl/go-wasm v0.0.1
|
||||
github.com/goplus/gogen v1.15.2
|
||||
github.com/goplus/llvm v0.7.1
|
||||
github.com/goplus/llvm v0.7.2
|
||||
github.com/goplus/mod v0.13.10
|
||||
github.com/qiniu/x v1.13.10
|
||||
golang.org/x/tools v0.20.0
|
||||
|
||||
2
go.sum
2
go.sum
@@ -4,6 +4,8 @@ github.com/goplus/gogen v1.15.2 h1:Q6XaSx/Zi5tWnjfAziYsQI6Jv6MgODRpFtOYqNkiiqM=
|
||||
github.com/goplus/gogen v1.15.2/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk=
|
||||
github.com/goplus/llvm v0.7.1 h1:B12Fr/wc3pAsq5PLuac9u9IuKpLRuCufdVAeGDP/MRw=
|
||||
github.com/goplus/llvm v0.7.1/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
|
||||
github.com/goplus/llvm v0.7.2 h1:NL3LlwAmYVCGA6yV40AjOvMDKl2dbCqoYPtugmLQK+E=
|
||||
github.com/goplus/llvm v0.7.2/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
|
||||
github.com/goplus/mod v0.13.10 h1:5Om6KOvo31daN7N30kWU1vC5zhsJPM+uPbcEN/FnlzE=
|
||||
github.com/goplus/mod v0.13.10/go.mod h1:HDuPZgpWiaTp3PUolFgsiX+Q77cbUWB/mikVHfYND3c=
|
||||
github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE=
|
||||
|
||||
636
internal/abi/llgo_autogen.ll
Normal file
636
internal/abi/llgo_autogen.ll
Normal file
@@ -0,0 +1,636 @@
|
||||
; ModuleID = 'github.com/goplus/llgo/internal/abi'
|
||||
source_filename = "github.com/goplus/llgo/internal/abi"
|
||||
|
||||
%"github.com/goplus/llgo/internal/abi.ArrayType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, i64 }
|
||||
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
|
||||
%"github.com/goplus/llgo/internal/abi.ChanType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, i64 }
|
||||
%"github.com/goplus/llgo/internal/abi.FuncType" = type { %"github.com/goplus/llgo/internal/abi.Type", i16, i16 }
|
||||
%"github.com/goplus/llgo/internal/abi.InterfaceType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" }
|
||||
%"github.com/goplus/llgo/internal/abi.Name" = type { ptr }
|
||||
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||
%"github.com/goplus/llgo/internal/abi.MapType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, ptr, ptr, i8, i8, i16, i32 }
|
||||
%"github.com/goplus/llgo/internal/abi.PtrType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr }
|
||||
%"github.com/goplus/llgo/internal/abi.SliceType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr }
|
||||
%"github.com/goplus/llgo/internal/abi.StructType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" }
|
||||
|
||||
@"github.com/goplus/llgo/internal/abi.init$guard" = global ptr null
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).ArrayType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).Common"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).FuncType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).InterfaceType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.ArrayType).Kind"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).MapType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).StructType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).ArrayType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).Common"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).FuncType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).InterfaceType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.ChanType).Kind"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.ChanType).Len"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).MapType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).StructType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).ArrayType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).Common"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).Elem"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Elem"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).FuncType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).InterfaceType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.FuncType).Kind"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.FuncType).Len"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).MapType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).StructType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).ArrayType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).Common"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).Elem"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Elem"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).FuncType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).InterfaceType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.InterfaceType).Kind"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.InterfaceType).Len"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).MapType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).StructType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).ArrayType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).Common"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).FuncType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).InterfaceType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.MapType).Kind"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.MapType).Len"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).MapType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).StructType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).ArrayType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).Common"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).FuncType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).InterfaceType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.PtrType).Kind"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.PtrType).Len"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).MapType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).StructType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).ArrayType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).Common"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).FuncType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).InterfaceType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.SliceType).Kind"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.SliceType).Len"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).MapType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).StructType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).ArrayType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).Common"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).Elem"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Elem"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).FuncType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).InterfaceType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.StructType).Kind"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.StructType).Len"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
|
||||
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).MapType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).StructType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
|
||||
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
|
||||
%2 = icmp ne i64 %1, 17
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret ptr null
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %0) {
|
||||
_llgo_0:
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).Elem"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
|
||||
%2 = icmp eq i64 %1, 17
|
||||
br i1 %2, label %_llgo_1, label %_llgo_3
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 1
|
||||
%4 = load ptr, ptr %3, align 8
|
||||
ret ptr %4
|
||||
|
||||
_llgo_2: ; preds = %_llgo_3
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 1
|
||||
%6 = load ptr, ptr %5, align 8
|
||||
ret ptr %6
|
||||
|
||||
_llgo_3: ; preds = %_llgo_0
|
||||
%7 = icmp eq i64 %1, 18
|
||||
br i1 %7, label %_llgo_2, label %_llgo_5
|
||||
|
||||
_llgo_4: ; preds = %_llgo_5
|
||||
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 2
|
||||
%9 = load ptr, ptr %8, align 8
|
||||
ret ptr %9
|
||||
|
||||
_llgo_5: ; preds = %_llgo_3
|
||||
%10 = icmp eq i64 %1, 21
|
||||
br i1 %10, label %_llgo_4, label %_llgo_7
|
||||
|
||||
_llgo_6: ; preds = %_llgo_7
|
||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 1
|
||||
%12 = load ptr, ptr %11, align 8
|
||||
ret ptr %12
|
||||
|
||||
_llgo_7: ; preds = %_llgo_5
|
||||
%13 = icmp eq i64 %1, 22
|
||||
br i1 %13, label %_llgo_6, label %_llgo_9
|
||||
|
||||
_llgo_8: ; preds = %_llgo_9
|
||||
%14 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 1
|
||||
%15 = load ptr, ptr %14, align 8
|
||||
ret ptr %15
|
||||
|
||||
_llgo_9: ; preds = %_llgo_7
|
||||
%16 = icmp eq i64 %1, 23
|
||||
br i1 %16, label %_llgo_8, label %_llgo_10
|
||||
|
||||
_llgo_10: ; preds = %_llgo_9
|
||||
ret ptr null
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
|
||||
%2 = icmp ne i64 %1, 19
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret ptr null
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
|
||||
%2 = icmp ne i64 %1, 20
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret ptr null
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %0, i32 0, i32 6
|
||||
%2 = load i8, ptr %1, align 1
|
||||
%3 = and i8 %2, 31
|
||||
%4 = sext i8 %3 to i64
|
||||
ret i64 %4
|
||||
}
|
||||
|
||||
define i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
|
||||
%2 = icmp eq i64 %1, 17
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 3
|
||||
%4 = load i64, ptr %3, align 4
|
||||
ret i64 %4
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret i64 0
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
|
||||
%2 = icmp ne i64 %1, 21
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret ptr null
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
|
||||
%2 = icmp ne i64 %1, 25
|
||||
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
ret ptr null
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret ptr %0
|
||||
}
|
||||
|
||||
define void @"github.com/goplus/llgo/internal/abi.init"() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"github.com/goplus/llgo/internal/abi.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"github.com/goplus/llgo/internal/abi.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
332
internal/abi/type.go
Normal file
332
internal/abi/type.go
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* 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 abi
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Type is the runtime representation of a Go type.
|
||||
//
|
||||
// Type is also referenced implicitly
|
||||
// (in the form of expressions involving constants and arch.PtrSize)
|
||||
// in cmd/compile/internal/reflectdata/reflect.go
|
||||
// and cmd/link/internal/ld/decodesym.go
|
||||
// (e.g. data[2*arch.PtrSize+4] references the TFlag field)
|
||||
// unsafe.OffsetOf(Type{}.TFlag) cannot be used directly in those
|
||||
// places because it varies with cross compilation and experiments.
|
||||
type Type struct {
|
||||
Size_ uintptr
|
||||
PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers
|
||||
Hash uint32 // hash of type; avoids computation in hash tables
|
||||
TFlag TFlag // extra type information flags
|
||||
Align_ uint8 // alignment of variable with this type
|
||||
FieldAlign_ uint8 // alignment of struct field with this type
|
||||
Kind_ uint8 // enumeration for C
|
||||
// function for comparing objects of this type
|
||||
// (ptr to object A, ptr to object B) -> ==?
|
||||
Equal func(unsafe.Pointer, unsafe.Pointer) bool
|
||||
// GCData stores the GC type data for the garbage collector.
|
||||
// If the KindGCProg bit is set in kind, GCData is a GC program.
|
||||
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
|
||||
GCData *byte
|
||||
Str NameOff // string form
|
||||
PtrToThis TypeOff // type for pointer to this type, may be zero
|
||||
}
|
||||
|
||||
func (t *Type) Kind() Kind { return Kind(t.Kind_ & KindMask) }
|
||||
|
||||
// A Kind represents the specific kind of type that a Type represents.
|
||||
// The zero Kind is not a valid kind.
|
||||
type Kind uint
|
||||
|
||||
const (
|
||||
Invalid Kind = iota
|
||||
Bool
|
||||
Int
|
||||
Int8
|
||||
Int16
|
||||
Int32
|
||||
Int64
|
||||
Uint
|
||||
Uint8
|
||||
Uint16
|
||||
Uint32
|
||||
Uint64
|
||||
Uintptr
|
||||
Float32
|
||||
Float64
|
||||
Complex64
|
||||
Complex128
|
||||
Array
|
||||
Chan
|
||||
Func
|
||||
Interface
|
||||
Map
|
||||
Pointer
|
||||
Slice
|
||||
String
|
||||
Struct
|
||||
UnsafePointer
|
||||
)
|
||||
|
||||
const (
|
||||
// TODO (khr, drchase) why aren't these in TFlag? Investigate, fix if possible.
|
||||
KindDirectIface = 1 << 5
|
||||
KindGCProg = 1 << 6 // Type.gc points to GC program
|
||||
KindMask = (1 << 5) - 1
|
||||
)
|
||||
|
||||
// TFlag is used by a Type to signal what extra type information is
|
||||
// available in the memory directly following the Type value.
|
||||
type TFlag uint8
|
||||
|
||||
const (
|
||||
// TFlagUncommon means that there is a data with a type, UncommonType,
|
||||
// just beyond the shared-per-type common data. That is, the data
|
||||
// for struct types will store their UncommonType at one offset, the
|
||||
// data for interface types will store their UncommonType at a different
|
||||
// offset. UncommonType is always accessed via a pointer that is computed
|
||||
// using trust-us-we-are-the-implementors pointer arithmetic.
|
||||
//
|
||||
// For example, if t.Kind() == Struct and t.tflag&TFlagUncommon != 0,
|
||||
// then t has UncommonType data and it can be accessed as:
|
||||
//
|
||||
// type structTypeUncommon struct {
|
||||
// structType
|
||||
// u UncommonType
|
||||
// }
|
||||
// u := &(*structTypeUncommon)(unsafe.Pointer(t)).u
|
||||
TFlagUncommon TFlag = 1 << 0
|
||||
|
||||
// TFlagExtraStar means the name in the str field has an
|
||||
// extraneous '*' prefix. This is because for most types T in
|
||||
// a program, the type *T also exists and reusing the str data
|
||||
// saves binary size.
|
||||
TFlagExtraStar TFlag = 1 << 1
|
||||
|
||||
// TFlagNamed means the type has a name.
|
||||
TFlagNamed TFlag = 1 << 2
|
||||
|
||||
// TFlagRegularMemory means that equal and hash functions can treat
|
||||
// this type as a single region of t.size bytes.
|
||||
TFlagRegularMemory TFlag = 1 << 3
|
||||
)
|
||||
|
||||
// NameOff is the offset to a name from moduledata.types. See resolveNameOff in runtime.
|
||||
type NameOff int32
|
||||
|
||||
// TypeOff is the offset to a type from moduledata.types. See resolveTypeOff in runtime.
|
||||
type TypeOff int32
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// ArrayType represents a fixed array type.
|
||||
type ArrayType struct {
|
||||
Type
|
||||
Elem *Type // array element type
|
||||
Slice *Type // slice type
|
||||
Len uintptr
|
||||
}
|
||||
|
||||
type SliceType struct {
|
||||
Type
|
||||
Elem *Type // slice element type
|
||||
}
|
||||
|
||||
type MapType struct {
|
||||
Type
|
||||
Key *Type
|
||||
Elem *Type
|
||||
Bucket *Type // internal type representing a hash bucket
|
||||
// function for hashing keys (ptr to key, seed) -> hash
|
||||
Hasher func(unsafe.Pointer, uintptr) uintptr
|
||||
KeySize uint8 // size of key slot
|
||||
ValueSize uint8 // size of elem slot
|
||||
BucketSize uint16 // size of bucket
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
type PtrType struct {
|
||||
Type
|
||||
Elem *Type // pointer element (pointed at) type
|
||||
}
|
||||
|
||||
type ChanDir int
|
||||
|
||||
const (
|
||||
RecvDir ChanDir = 1 << iota // <-chan
|
||||
SendDir // chan<-
|
||||
BothDir = RecvDir | SendDir // chan
|
||||
InvalidDir ChanDir = 0
|
||||
)
|
||||
|
||||
// ChanType represents a channel type
|
||||
type ChanType struct {
|
||||
Type
|
||||
Elem *Type
|
||||
Dir ChanDir
|
||||
}
|
||||
|
||||
// funcType represents a function type.
|
||||
//
|
||||
// A *Type for each in and out parameter is stored in an array that
|
||||
// directly follows the funcType (and possibly its uncommonType). So
|
||||
// a function type with one method, one input, and one output is:
|
||||
//
|
||||
// struct {
|
||||
// funcType
|
||||
// uncommonType
|
||||
// [2]*rtype // [0] is in, [1] is out
|
||||
// }
|
||||
type FuncType struct {
|
||||
Type
|
||||
InCount uint16
|
||||
OutCount uint16 // top bit is set if last input parameter is ...
|
||||
}
|
||||
|
||||
type StructField struct {
|
||||
Name Name // name is always non-empty
|
||||
Typ *Type // type of field
|
||||
Offset uintptr // byte offset of field
|
||||
}
|
||||
|
||||
type StructType struct {
|
||||
Type
|
||||
PkgPath Name
|
||||
Fields []StructField
|
||||
}
|
||||
|
||||
// Name is an encoded type Name with optional extra data.
|
||||
//
|
||||
// The first byte is a bit field containing:
|
||||
//
|
||||
// 1<<0 the name is exported
|
||||
// 1<<1 tag data follows the name
|
||||
// 1<<2 pkgPath nameOff follows the name and tag
|
||||
// 1<<3 the name is of an embedded (a.k.a. anonymous) field
|
||||
//
|
||||
// Following that, there is a varint-encoded length of the name,
|
||||
// followed by the name itself.
|
||||
//
|
||||
// If tag data is present, it also has a varint-encoded length
|
||||
// followed by the tag itself.
|
||||
//
|
||||
// If the import path follows, then 4 bytes at the end of
|
||||
// the data form a nameOff. The import path is only set for concrete
|
||||
// methods that are defined in a different package than their type.
|
||||
//
|
||||
// If a name starts with "*", then the exported bit represents
|
||||
// whether the pointed to type is exported.
|
||||
//
|
||||
// Note: this encoding must match here and in:
|
||||
// cmd/compile/internal/reflectdata/reflect.go
|
||||
// cmd/link/internal/ld/decodesym.go
|
||||
|
||||
type Name struct {
|
||||
Bytes *byte
|
||||
}
|
||||
|
||||
type InterfaceType struct {
|
||||
Type
|
||||
PkgPath Name // import path
|
||||
Methods []Imethod // sorted by hash
|
||||
}
|
||||
|
||||
// Imethod represents a method on an interface type
|
||||
type Imethod struct {
|
||||
Name NameOff // name of method
|
||||
Typ TypeOff // .(*FuncType) underneath
|
||||
}
|
||||
|
||||
func (t *Type) Common() *Type {
|
||||
return t
|
||||
}
|
||||
|
||||
// Len returns the length of t if t is an array type, otherwise 0
|
||||
func (t *Type) Len() int {
|
||||
if t.Kind() == Array {
|
||||
return int((*ArrayType)(unsafe.Pointer(t)).Len)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil.
|
||||
func (t *Type) Elem() *Type {
|
||||
switch t.Kind() {
|
||||
case Array:
|
||||
tt := (*ArrayType)(unsafe.Pointer(t))
|
||||
return tt.Elem
|
||||
case Chan:
|
||||
tt := (*ChanType)(unsafe.Pointer(t))
|
||||
return tt.Elem
|
||||
case Map:
|
||||
tt := (*MapType)(unsafe.Pointer(t))
|
||||
return tt.Elem
|
||||
case Pointer:
|
||||
tt := (*PtrType)(unsafe.Pointer(t))
|
||||
return tt.Elem
|
||||
case Slice:
|
||||
tt := (*SliceType)(unsafe.Pointer(t))
|
||||
return tt.Elem
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StructType returns t cast to a *StructType, or nil if its tag does not match.
|
||||
func (t *Type) StructType() *StructType {
|
||||
if t.Kind() != Struct {
|
||||
return nil
|
||||
}
|
||||
return (*StructType)(unsafe.Pointer(t))
|
||||
}
|
||||
|
||||
// MapType returns t cast to a *MapType, or nil if its tag does not match.
|
||||
func (t *Type) MapType() *MapType {
|
||||
if t.Kind() != Map {
|
||||
return nil
|
||||
}
|
||||
return (*MapType)(unsafe.Pointer(t))
|
||||
}
|
||||
|
||||
// ArrayType returns t cast to a *ArrayType, or nil if its tag does not match.
|
||||
func (t *Type) ArrayType() *ArrayType {
|
||||
if t.Kind() != Array {
|
||||
return nil
|
||||
}
|
||||
return (*ArrayType)(unsafe.Pointer(t))
|
||||
}
|
||||
|
||||
// FuncType returns t cast to a *FuncType, or nil if its tag does not match.
|
||||
func (t *Type) FuncType() *FuncType {
|
||||
if t.Kind() != Func {
|
||||
return nil
|
||||
}
|
||||
return (*FuncType)(unsafe.Pointer(t))
|
||||
}
|
||||
|
||||
// InterfaceType returns t cast to a *InterfaceType, or nil if its tag does not match.
|
||||
func (t *Type) InterfaceType() *InterfaceType {
|
||||
if t.Kind() != Interface {
|
||||
return nil
|
||||
}
|
||||
return (*InterfaceType)(unsafe.Pointer(t))
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -19,7 +19,7 @@ package build
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"log"
|
||||
"go/types"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -50,8 +50,9 @@ func needLLFile(mode Mode) bool {
|
||||
|
||||
type Config struct {
|
||||
BinPath string
|
||||
AppExt string // ".exe" on Windows, empty on Unix
|
||||
RunArgs []string
|
||||
AppExt string // ".exe" on Windows, empty on Unix
|
||||
OutFile string // only valid for ModeBuild when len(pkgs) == 1
|
||||
RunArgs []string // only valid for ModeRun
|
||||
Mode Mode
|
||||
}
|
||||
|
||||
@@ -85,9 +86,9 @@ const (
|
||||
)
|
||||
|
||||
func Do(args []string, conf *Config) {
|
||||
flags, patterns, verbose := parseArgs(args)
|
||||
flags, patterns, verbose := ParseArgs(args, buildFlags)
|
||||
cfg := &packages.Config{
|
||||
Mode: loadSyntax | packages.NeedDeps | packages.NeedExportFile,
|
||||
Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile,
|
||||
BuildFlags: flags,
|
||||
}
|
||||
|
||||
@@ -97,11 +98,18 @@ func Do(args []string, conf *Config) {
|
||||
initial, err := packages.Load(cfg, patterns...)
|
||||
check(err)
|
||||
|
||||
// Create SSA-form program representation.
|
||||
ssaProg, pkgs, errPkgs := allPkgs(initial, ssa.SanityCheckFunctions)
|
||||
ssaProg.Build()
|
||||
for _, errPkg := range errPkgs {
|
||||
log.Println("cannot build SSA for package", errPkg)
|
||||
mode := conf.Mode
|
||||
if len(initial) == 1 && len(initial[0].CompiledGoFiles) > 0 {
|
||||
if mode == ModeBuild {
|
||||
mode = ModeInstall
|
||||
}
|
||||
} else if mode == ModeRun {
|
||||
if len(initial) > 1 {
|
||||
fmt.Fprintln(os.Stderr, "cannot run multiple packages")
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "no Go files in matched packages")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
llssa.Initialize(llssa.InitAll)
|
||||
@@ -110,37 +118,93 @@ func Do(args []string, conf *Config) {
|
||||
cl.SetDebug(cl.DbgFlagAll)
|
||||
}
|
||||
|
||||
var rt []*packages.Package
|
||||
prog := llssa.NewProgram(nil)
|
||||
mode := conf.Mode
|
||||
for _, pkg := range pkgs {
|
||||
buildPkg(prog, pkg, mode)
|
||||
}
|
||||
prog.SetRuntime(func() *types.Package {
|
||||
rt, err = packages.Load(cfg, llssa.PkgRuntime)
|
||||
check(err)
|
||||
return rt[0].Types
|
||||
})
|
||||
|
||||
if mode != ModeBuild || len(initial) == 1 {
|
||||
buildAllPkgs(prog, initial, mode, verbose)
|
||||
|
||||
var runtimeFiles []string
|
||||
if rt != nil {
|
||||
runtimeFiles = allLinkFiles(rt)
|
||||
}
|
||||
if mode != ModeBuild {
|
||||
nErr := 0
|
||||
for _, pkg := range initial {
|
||||
if pkg.Name == "main" {
|
||||
linkMainPkg(pkg, conf, mode)
|
||||
nErr += linkMainPkg(pkg, runtimeFiles, conf, mode, verbose)
|
||||
}
|
||||
}
|
||||
if nErr > 0 {
|
||||
fmt.Fprintf(os.Stderr, "%d errors occurred\n", nErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func linkMainPkg(pkg *packages.Package, conf *Config, mode Mode) {
|
||||
func setNeedRuntime(pkg *packages.Package) {
|
||||
pkg.ID = "" // just use pkg.Module to mark it needs runtime
|
||||
}
|
||||
|
||||
func isNeedRuntime(pkg *packages.Package) bool {
|
||||
return pkg.ID == ""
|
||||
}
|
||||
|
||||
func buildAllPkgs(prog llssa.Program, initial []*packages.Package, mode Mode, verbose bool) {
|
||||
// Create SSA-form program representation.
|
||||
ssaProg, pkgs, errPkgs := allPkgs(initial, ssa.SanityCheckFunctions)
|
||||
ssaProg.Build()
|
||||
for _, errPkg := range errPkgs {
|
||||
fmt.Fprintln(os.Stderr, "cannot build SSA for package", errPkg)
|
||||
}
|
||||
for _, pkg := range pkgs {
|
||||
buildPkg(prog, pkg, mode, verbose)
|
||||
if prog.NeedRuntime() {
|
||||
setNeedRuntime(pkg.Package)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func linkMainPkg(pkg *packages.Package, runtimeFiles []string, conf *Config, mode Mode, verbose bool) (nErr int) {
|
||||
pkgPath := pkg.PkgPath
|
||||
name := path.Base(pkgPath)
|
||||
app := filepath.Join(conf.BinPath, name+conf.AppExt)
|
||||
args := make([]string, 2, len(pkg.Imports)+3)
|
||||
app := conf.OutFile
|
||||
if app == "" {
|
||||
app = filepath.Join(conf.BinPath, name+conf.AppExt)
|
||||
}
|
||||
const N = 3
|
||||
args := make([]string, N, len(pkg.Imports)+len(runtimeFiles)+(N+1))
|
||||
args[0] = "-o"
|
||||
args[1] = app
|
||||
args[2] = "-Wno-override-module"
|
||||
needRuntime := false
|
||||
packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) {
|
||||
if p.PkgPath != "unsafe" { // TODO(xsw): remove this special case
|
||||
if p.PkgPath != "unsafe" { // TODO(xsw): maybe can remove this special case
|
||||
args = append(args, p.ExportFile+".ll")
|
||||
if !needRuntime {
|
||||
needRuntime = isNeedRuntime(p)
|
||||
}
|
||||
}
|
||||
})
|
||||
if needRuntime && runtimeFiles != nil {
|
||||
args = append(args, runtimeFiles...)
|
||||
}
|
||||
|
||||
if verbose || mode != ModeRun {
|
||||
fmt.Fprintln(os.Stderr, "#", pkgPath)
|
||||
}
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
nErr = 1
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO(xsw): show work
|
||||
// fmt.Fprintln(os.Stderr, "clang", args)
|
||||
fmt.Fprintln(os.Stderr, "#", pkgPath)
|
||||
err := clang.New("").Exec(args...)
|
||||
check(err)
|
||||
|
||||
@@ -151,15 +215,16 @@ func linkMainPkg(pkg *packages.Package, conf *Config, mode Mode) {
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Run()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func buildPkg(prog llssa.Program, aPkg aPackage, mode Mode) {
|
||||
func buildPkg(prog llssa.Program, aPkg aPackage, mode Mode, verbose bool) {
|
||||
pkg := aPkg.Package
|
||||
pkgPath := pkg.PkgPath
|
||||
if mode != ModeRun {
|
||||
if verbose {
|
||||
fmt.Fprintln(os.Stderr, pkgPath)
|
||||
}
|
||||
if pkgPath == "unsafe" { // TODO(xsw): remove this special case
|
||||
if pkgPath == "unsafe" { // TODO(xsw): maybe can remove this special case
|
||||
return
|
||||
}
|
||||
ret, err := cl.NewPackage(prog, aPkg.SSA, pkg.Syntax)
|
||||
@@ -193,20 +258,97 @@ func allPkgs(initial []*packages.Package, mode ssa.BuilderMode) (prog *ssa.Progr
|
||||
return
|
||||
}
|
||||
|
||||
func parseArgs(args []string) (flags, patterns []string, verbose bool) {
|
||||
for i, arg := range args {
|
||||
if !strings.HasPrefix(arg, "-") {
|
||||
var (
|
||||
// TODO(xsw): complete build flags
|
||||
buildFlags = map[string]bool{
|
||||
"-C": true, // -C dir: Change to dir before running the command
|
||||
"-a": false, // -a: force rebuilding of packages that are already up-to-date
|
||||
"-n": false, // -n: print the commands but do not run them
|
||||
"-p": true, // -p n: the number of programs to run in parallel
|
||||
"-race": false, // -race: enable data race detection
|
||||
"-cover": false, // -cover: enable coverage analysis
|
||||
"-covermode": true, // -covermode mode: set the mode for coverage analysis
|
||||
"-v": false, // -v: print the names of packages as they are compiled
|
||||
"-work": false, // -work: print the name of the temporary work directory and do not delete it when exiting
|
||||
"-x": false, // -x: print the commands
|
||||
"-tags": true, // -tags 'tag,list': a space-separated list of build tags to consider satisfied during the build
|
||||
"-pkgdir": true, // -pkgdir dir: install and load all packages from dir instead of the usual locations
|
||||
}
|
||||
)
|
||||
|
||||
func ParseArgs(args []string, swflags map[string]bool) (flags, patterns []string, verbose bool) {
|
||||
n := len(args)
|
||||
for i := 0; i < n; i++ {
|
||||
arg := args[i]
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
checkFlag(arg, &i, &verbose, swflags)
|
||||
} else {
|
||||
flags, patterns = args[:i], args[i:]
|
||||
return
|
||||
}
|
||||
if arg == "-v" {
|
||||
verbose = true
|
||||
}
|
||||
}
|
||||
flags = args
|
||||
return
|
||||
}
|
||||
|
||||
func SkipFlagArgs(args []string) int {
|
||||
n := len(args)
|
||||
for i := 0; i < n; i++ {
|
||||
arg := args[i]
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
checkFlag(arg, &i, nil, buildFlags)
|
||||
} else {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func checkFlag(arg string, i *int, verbose *bool, swflags map[string]bool) {
|
||||
if hasarg, ok := swflags[arg]; ok {
|
||||
if hasarg {
|
||||
*i++
|
||||
} else if verbose != nil && arg == "-v" {
|
||||
*verbose = true
|
||||
}
|
||||
} else {
|
||||
panic("unknown flag: " + arg)
|
||||
}
|
||||
}
|
||||
|
||||
func allLinkFiles(rt []*packages.Package) (outFiles []string) {
|
||||
outFiles = make([]string, 0, len(rt))
|
||||
root := rootLLGo(rt[0])
|
||||
packages.Visit(rt, nil, func(p *packages.Package) {
|
||||
if isPkgInLLGo(p.PkgPath) {
|
||||
outFile := filepath.Join(root+p.PkgPath[len(llgoModPath):], "llgo_autogen.ll")
|
||||
outFiles = append(outFiles, outFile)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(xsw): llgo root dir
|
||||
func rootLLGo(runtime *packages.Package) string {
|
||||
return runtime.Module.Dir
|
||||
}
|
||||
|
||||
const (
|
||||
llgoModPath = "github.com/goplus/llgo"
|
||||
)
|
||||
|
||||
func isPkgInLLGo(pkgPath string) bool {
|
||||
return isPkgInMod(pkgPath, llgoModPath)
|
||||
}
|
||||
|
||||
func isPkgInMod(pkgPath, modPath string) bool {
|
||||
if strings.HasPrefix(pkgPath, modPath) {
|
||||
suffix := pkgPath[len(modPath):]
|
||||
return suffix == "" || suffix[0] == '/'
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
85
internal/build/clean.go
Normal file
85
internal/build/clean.go
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO(xsw): complete clean flags
|
||||
cleanFlags = map[string]bool{
|
||||
"-v": false, // -v: print the paths of packages as they are clean
|
||||
}
|
||||
)
|
||||
|
||||
func Clean(args []string, conf *Config) {
|
||||
flags, patterns, verbose := ParseArgs(args, cleanFlags)
|
||||
cfg := &packages.Config{
|
||||
Mode: loadSyntax | packages.NeedExportFile,
|
||||
BuildFlags: flags,
|
||||
}
|
||||
|
||||
if patterns == nil {
|
||||
patterns = []string{"."}
|
||||
}
|
||||
initial, err := packages.Load(cfg, patterns...)
|
||||
check(err)
|
||||
|
||||
cleanPkgs(initial, verbose)
|
||||
|
||||
for _, pkg := range initial {
|
||||
if pkg.Name == "main" {
|
||||
cleanMainPkg(pkg, conf, verbose)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cleanMainPkg(pkg *packages.Package, conf *Config, verbose bool) {
|
||||
pkgPath := pkg.PkgPath
|
||||
name := path.Base(pkgPath)
|
||||
fname := name + conf.AppExt
|
||||
app := filepath.Join(conf.BinPath, fname)
|
||||
removeFile(app, verbose)
|
||||
if len(pkg.CompiledGoFiles) > 0 {
|
||||
dir := filepath.Dir(pkg.CompiledGoFiles[0])
|
||||
buildApp := filepath.Join(dir, fname)
|
||||
removeFile(buildApp, verbose)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanPkgs(initial []*packages.Package, verbose bool) {
|
||||
packages.Visit(initial, nil, func(p *packages.Package) {
|
||||
file := p.ExportFile + ".ll"
|
||||
removeFile(file, verbose)
|
||||
})
|
||||
}
|
||||
|
||||
func removeFile(file string, verbose bool) {
|
||||
if _, err := os.Stat(file); os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
if verbose {
|
||||
fmt.Fprintln(os.Stderr, "Remove", file)
|
||||
}
|
||||
os.Remove(file)
|
||||
}
|
||||
68
internal/llgen/llgenf.go
Normal file
68
internal/llgen/llgenf.go
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 llgen
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"os"
|
||||
|
||||
"github.com/goplus/llgo/cl"
|
||||
"golang.org/x/tools/go/packages"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
)
|
||||
|
||||
const (
|
||||
loadFiles = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles
|
||||
loadImports = loadFiles | packages.NeedImports
|
||||
loadTypes = loadImports | packages.NeedTypes | packages.NeedTypesSizes
|
||||
loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo
|
||||
)
|
||||
|
||||
func GenFrom(fileOrPkg string) string {
|
||||
cfg := &packages.Config{
|
||||
Mode: loadSyntax,
|
||||
}
|
||||
initial, err := packages.Load(cfg, fileOrPkg)
|
||||
check(err)
|
||||
|
||||
_, pkgs := ssautil.AllPackages(initial, ssa.SanityCheckFunctions)
|
||||
|
||||
pkg := initial[0]
|
||||
ssaPkg := pkgs[0]
|
||||
ssaPkg.Build()
|
||||
|
||||
prog := llssa.NewProgram(nil)
|
||||
prog.SetRuntime(func() *types.Package {
|
||||
rt, err := packages.Load(cfg, llssa.PkgRuntime)
|
||||
check(err)
|
||||
return rt[0].Types
|
||||
})
|
||||
|
||||
ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax)
|
||||
check(err)
|
||||
|
||||
return ret.String()
|
||||
}
|
||||
|
||||
func DoFile(fileOrPkg, outFile string) {
|
||||
ret := GenFrom(fileOrPkg)
|
||||
err := os.WriteFile(outFile, []byte(ret), 0644)
|
||||
check(err)
|
||||
}
|
||||
270
internal/runtime/llgo_autogen.ll
Normal file
270
internal/runtime/llgo_autogen.ll
Normal file
@@ -0,0 +1,270 @@
|
||||
; ModuleID = 'github.com/goplus/llgo/internal/runtime'
|
||||
source_filename = "github.com/goplus/llgo/internal/runtime"
|
||||
|
||||
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
|
||||
%"github.com/goplus/llgo/internal/runtime.itab" = type { ptr, ptr, i32, [4 x i8], [1 x i64] }
|
||||
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
||||
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
|
||||
|
||||
@"github.com/goplus/llgo/internal/runtime.TyAny" = global ptr null
|
||||
@"github.com/goplus/llgo/internal/runtime.basicTypes" = global ptr null
|
||||
@"github.com/goplus/llgo/internal/runtime.init$guard" = global ptr null
|
||||
@"github.com/goplus/llgo/internal/runtime.sizeBasicTypes" = global ptr null
|
||||
|
||||
define ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 %0) {
|
||||
_llgo_0:
|
||||
%1 = call ptr @malloc(i64 %0)
|
||||
ret ptr %1
|
||||
}
|
||||
|
||||
define ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 %0) {
|
||||
_llgo_0:
|
||||
%1 = getelementptr inbounds ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 %0
|
||||
%2 = load ptr, ptr %1, align 8
|
||||
ret ptr %2
|
||||
}
|
||||
|
||||
define { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
|
||||
_llgo_0:
|
||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %2, align 8
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
|
||||
%4 = load ptr, ptr %3, align 8
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
|
||||
%6 = load ptr, ptr %5, align 8
|
||||
%7 = icmp eq ptr %6, %1
|
||||
br i1 %7, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
|
||||
%9 = load ptr, ptr %8, align 8
|
||||
%10 = ptrtoint ptr %9 to i64
|
||||
%mrv = insertvalue { i64, i1 } poison, i64 %10, 0
|
||||
%mrv1 = insertvalue { i64, i1 } %mrv, i1 true, 1
|
||||
ret { i64, i1 } %mrv1
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
ret { i64, i1 } zeroinitializer
|
||||
}
|
||||
|
||||
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.EmptyString"() {
|
||||
_llgo_0:
|
||||
%0 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 0
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 1
|
||||
store ptr null, ptr %1, align 8
|
||||
store i64 0, ptr %2, align 4
|
||||
%3 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %0, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.String" %3
|
||||
}
|
||||
|
||||
define i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
|
||||
_llgo_0:
|
||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %2, align 8
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
|
||||
%4 = load ptr, ptr %3, align 8
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
|
||||
%6 = load ptr, ptr %5, align 8
|
||||
%7 = icmp eq ptr %6, %1
|
||||
br i1 %7, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
|
||||
%9 = load ptr, ptr %8, align 8
|
||||
%10 = ptrtoint ptr %9 to i64
|
||||
ret i64 %10
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
%11 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"([21 x i8] c"I2Int: type mismatch\00")
|
||||
unreachable
|
||||
}
|
||||
|
||||
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAny"(ptr %0, ptr %1) {
|
||||
_llgo_0:
|
||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
|
||||
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 0
|
||||
%6 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
||||
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
|
||||
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 2
|
||||
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 4
|
||||
%10 = getelementptr inbounds i64, ptr %9, i64 0
|
||||
store ptr %6, ptr %5, align 8
|
||||
store ptr %0, ptr %7, align 8
|
||||
store i32 0, ptr %8, align 4
|
||||
store i64 0, ptr %10, align 4
|
||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
|
||||
store ptr %4, ptr %3, align 8
|
||||
store ptr %1, ptr %11, align 8
|
||||
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
|
||||
}
|
||||
|
||||
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %0, i64 %1) {
|
||||
_llgo_0:
|
||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
|
||||
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 0
|
||||
%6 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
||||
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
|
||||
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 2
|
||||
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 4
|
||||
%10 = getelementptr inbounds i64, ptr %9, i64 0
|
||||
store ptr %6, ptr %5, align 8
|
||||
store ptr %0, ptr %7, align 8
|
||||
store i32 0, ptr %8, align 4
|
||||
store i64 0, ptr %10, align 4
|
||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
|
||||
%12 = inttoptr i64 %1 to ptr
|
||||
store ptr %4, ptr %3, align 8
|
||||
store ptr %12, ptr %11, align 8
|
||||
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
|
||||
}
|
||||
|
||||
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
||||
_llgo_0:
|
||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
|
||||
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %1, align 8
|
||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
|
||||
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 0
|
||||
%6 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
||||
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
|
||||
%8 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 24)
|
||||
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 2
|
||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 4
|
||||
%11 = getelementptr inbounds i64, ptr %10, i64 0
|
||||
store ptr %6, ptr %5, align 8
|
||||
store ptr %8, ptr %7, align 8
|
||||
store i32 0, ptr %9, align 4
|
||||
store i64 0, ptr %11, align 4
|
||||
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
|
||||
store ptr %4, ptr %3, align 8
|
||||
store ptr %1, ptr %12, align 8
|
||||
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
|
||||
}
|
||||
|
||||
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeInterface"(ptr %0, ptr %1, ptr %2) {
|
||||
_llgo_0:
|
||||
%3 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 0
|
||||
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
|
||||
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 0
|
||||
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 1
|
||||
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 2
|
||||
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 4
|
||||
%10 = getelementptr inbounds i64, ptr %9, i64 0
|
||||
store ptr %0, ptr %6, align 8
|
||||
store ptr %1, ptr %7, align 8
|
||||
store i32 0, ptr %8, align 4
|
||||
store i64 0, ptr %10, align 4
|
||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 1
|
||||
store ptr %5, ptr %4, align 8
|
||||
store ptr %2, ptr %11, align 8
|
||||
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
|
||||
}
|
||||
|
||||
declare ptr @malloc(i64)
|
||||
|
||||
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NilSlice"() {
|
||||
_llgo_0:
|
||||
%0 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, i32 0, i32 0
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, i32 0, i32 1
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, i32 0, i32 2
|
||||
store ptr null, ptr %1, align 8
|
||||
store i64 0, ptr %2, align 4
|
||||
store i64 0, ptr %3, align 4
|
||||
%4 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.Slice" %4
|
||||
}
|
||||
|
||||
define ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 %0) {
|
||||
_llgo_0:
|
||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 0
|
||||
%3 = getelementptr inbounds i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 %0
|
||||
%4 = load i64, ptr %3, align 4
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 6
|
||||
%6 = trunc i64 %0 to i8
|
||||
store i64 %4, ptr %2, align 4
|
||||
store i8 %6, ptr %5, align 1
|
||||
ret ptr %1
|
||||
}
|
||||
|
||||
define void @"github.com/goplus/llgo/internal/runtime.init"() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"github.com/goplus/llgo/internal/runtime.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"github.com/goplus/llgo/internal/runtime.init$guard", align 1
|
||||
call void @"github.com/goplus/llgo/internal/abi.init"()
|
||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
|
||||
store ptr %1, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
||||
store i64 1, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 1), align 4
|
||||
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 2), align 4
|
||||
store i64 1, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 3), align 4
|
||||
store i64 2, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 4), align 4
|
||||
store i64 4, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 5), align 4
|
||||
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 6), align 4
|
||||
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 7), align 4
|
||||
store i64 1, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 8), align 4
|
||||
store i64 2, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 9), align 4
|
||||
store i64 4, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 10), align 4
|
||||
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 11), align 4
|
||||
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 12), align 4
|
||||
store i64 4, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 13), align 4
|
||||
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 14), align 4
|
||||
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 15), align 4
|
||||
store i64 16, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 16), align 4
|
||||
store i64 16, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 24), align 4
|
||||
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 1)
|
||||
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 2)
|
||||
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 3)
|
||||
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 4)
|
||||
%6 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 5)
|
||||
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 6)
|
||||
%8 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 7)
|
||||
%9 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 8)
|
||||
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 9)
|
||||
%11 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 10)
|
||||
%12 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 11)
|
||||
%13 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 12)
|
||||
%14 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 13)
|
||||
%15 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 14)
|
||||
%16 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 15)
|
||||
%17 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 16)
|
||||
%18 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 24)
|
||||
store ptr %2, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 1), align 8
|
||||
store ptr %3, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 2), align 8
|
||||
store ptr %4, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 3), align 8
|
||||
store ptr %5, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 4), align 8
|
||||
store ptr %6, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 5), align 8
|
||||
store ptr %7, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 6), align 8
|
||||
store ptr %8, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 7), align 8
|
||||
store ptr %9, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 8), align 8
|
||||
store ptr %10, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 9), align 8
|
||||
store ptr %11, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 10), align 8
|
||||
store ptr %12, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 11), align 8
|
||||
store ptr %13, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 12), align 8
|
||||
store ptr %14, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 13), align 8
|
||||
store ptr %15, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 14), align 8
|
||||
store ptr %16, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 15), align 8
|
||||
store ptr %17, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 16), align 8
|
||||
store ptr %18, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 24), align 8
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/abi.init"()
|
||||
37
internal/runtime/runtime2.go
Normal file
37
internal/runtime/runtime2.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type iface struct {
|
||||
tab *itab
|
||||
data unsafe.Pointer
|
||||
}
|
||||
|
||||
/*
|
||||
type eface struct {
|
||||
_type *_type
|
||||
data unsafe.Pointer
|
||||
}
|
||||
|
||||
func efaceOf(ep *any) *eface {
|
||||
return (*eface)(unsafe.Pointer(ep))
|
||||
}
|
||||
*/
|
||||
|
||||
// layout of Itab known to compilers
|
||||
// allocated in non-garbage-collected memory
|
||||
// Needs to be in sync with
|
||||
// ../cmd/compile/internal/reflectdata/reflect.go:/^func.WriteTabs.
|
||||
type itab struct {
|
||||
inter *interfacetype
|
||||
_type *_type
|
||||
hash uint32 // copy of _type.hash. Used for type switches.
|
||||
_ [4]byte
|
||||
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
|
||||
}
|
||||
18
internal/runtime/type.go
Normal file
18
internal/runtime/type.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Runtime type representation.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/internal/abi"
|
||||
)
|
||||
|
||||
// type nameOff = abi.NameOff
|
||||
// type typeOff = abi.TypeOff
|
||||
|
||||
type _type = abi.Type
|
||||
|
||||
type interfacetype = abi.InterfaceType
|
||||
31
internal/runtime/z_gc.go
Normal file
31
internal/runtime/z_gc.go
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 runtime
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const (
|
||||
LLGoPackage = true
|
||||
)
|
||||
|
||||
//go:linkname Malloc C.malloc
|
||||
func Malloc(size uintptr) unsafe.Pointer
|
||||
|
||||
// Alloc allocates memory.
|
||||
func Alloc(size uintptr) unsafe.Pointer {
|
||||
return Malloc(size)
|
||||
}
|
||||
73
internal/runtime/z_iface.go
Normal file
73
internal/runtime/z_iface.go
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/internal/abi"
|
||||
)
|
||||
|
||||
type Interface = iface
|
||||
|
||||
type InterfaceType = abi.InterfaceType
|
||||
|
||||
var (
|
||||
TyAny = &InterfaceType{}
|
||||
)
|
||||
|
||||
func MakeAnyInt(typ *Type, data uintptr) Interface {
|
||||
return Interface{
|
||||
tab: &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}},
|
||||
data: unsafe.Pointer(data),
|
||||
}
|
||||
}
|
||||
|
||||
func MakeAnyString(data string) Interface {
|
||||
return Interface{
|
||||
tab: &itab{inter: TyAny, _type: Basic(abi.String), hash: 0, fun: [1]uintptr{0}},
|
||||
data: unsafe.Pointer(&data),
|
||||
}
|
||||
}
|
||||
|
||||
func MakeAny(typ *Type, data unsafe.Pointer) Interface {
|
||||
return Interface{
|
||||
tab: &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}},
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func MakeInterface(inter *InterfaceType, typ *Type, data unsafe.Pointer) Interface {
|
||||
return Interface{
|
||||
tab: &itab{inter: inter, _type: typ, hash: 0, fun: [1]uintptr{0}},
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func I2Int(v Interface, t *Type) uintptr {
|
||||
if v.tab._type == t {
|
||||
return uintptr(v.data)
|
||||
}
|
||||
panic("I2Int: type mismatch")
|
||||
}
|
||||
|
||||
func CheckI2Int(v Interface, t *Type) (uintptr, bool) {
|
||||
if v.tab._type == t {
|
||||
return uintptr(v.data), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
33
internal/runtime/z_slice.go
Normal file
33
internal/runtime/z_slice.go
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Slice is the runtime representation of a slice.
|
||||
type Slice struct {
|
||||
array unsafe.Pointer
|
||||
len int
|
||||
cap int
|
||||
}
|
||||
|
||||
// NilSlice returns a nil slice.
|
||||
func NilSlice() Slice {
|
||||
return Slice{nil, 0, 0}
|
||||
}
|
||||
37
internal/runtime/z_string.go
Normal file
37
internal/runtime/z_string.go
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// String is the runtime representation of a string.
|
||||
// It cannot be used safely or portably and its representation may
|
||||
// change in a later release.
|
||||
//
|
||||
// Unlike reflect.StringHeader, its Data field is sufficient to guarantee the
|
||||
// data it references will not be garbage collected.
|
||||
type String struct {
|
||||
data unsafe.Pointer
|
||||
len int
|
||||
}
|
||||
|
||||
// EmptyString returns an empty string.
|
||||
func EmptyString() String {
|
||||
return String{nil, 0}
|
||||
}
|
||||
81
internal/runtime/z_type.go
Normal file
81
internal/runtime/z_type.go
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/internal/abi"
|
||||
)
|
||||
|
||||
type Kind = abi.Kind
|
||||
type Type = abi.Type
|
||||
|
||||
func Basic(kind Kind) *Type {
|
||||
return basicTypes[kind]
|
||||
}
|
||||
|
||||
var (
|
||||
basicTypes = [...]*Type{
|
||||
abi.Bool: basicType(abi.Bool),
|
||||
abi.Int: basicType(abi.Int),
|
||||
abi.Int8: basicType(abi.Int8),
|
||||
abi.Int16: basicType(abi.Int16),
|
||||
abi.Int32: basicType(abi.Int32),
|
||||
abi.Int64: basicType(abi.Int64),
|
||||
abi.Uint: basicType(abi.Uint),
|
||||
abi.Uint8: basicType(abi.Uint8),
|
||||
abi.Uint16: basicType(abi.Uint16),
|
||||
abi.Uint32: basicType(abi.Uint32),
|
||||
abi.Uint64: basicType(abi.Uint64),
|
||||
abi.Uintptr: basicType(abi.Uintptr),
|
||||
abi.Float32: basicType(abi.Float32),
|
||||
abi.Float64: basicType(abi.Float64),
|
||||
abi.Complex64: basicType(abi.Complex64),
|
||||
abi.Complex128: basicType(abi.Complex128),
|
||||
abi.String: basicType(abi.String),
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
sizeBasicTypes = [...]uintptr{
|
||||
abi.Bool: unsafe.Sizeof(false),
|
||||
abi.Int: unsafe.Sizeof(0),
|
||||
abi.Int8: 1,
|
||||
abi.Int16: 2,
|
||||
abi.Int32: 4,
|
||||
abi.Int64: 8,
|
||||
abi.Uint: unsafe.Sizeof(uint(0)),
|
||||
abi.Uint8: 1,
|
||||
abi.Uint16: 2,
|
||||
abi.Uint32: 4,
|
||||
abi.Uint64: 8,
|
||||
abi.Uintptr: unsafe.Sizeof(uintptr(0)),
|
||||
abi.Float32: 4,
|
||||
abi.Float64: 8,
|
||||
abi.Complex64: 8,
|
||||
abi.Complex128: 16,
|
||||
abi.String: unsafe.Sizeof(String{}),
|
||||
}
|
||||
)
|
||||
|
||||
func basicType(kind abi.Kind) *Type {
|
||||
return &abi.Type{
|
||||
Size_: sizeBasicTypes[kind],
|
||||
Kind_: uint8(kind),
|
||||
}
|
||||
}
|
||||
36
ssa/cl_test.go
Normal file
36
ssa/cl_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 ssa_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goplus/llgo/cl/cltest"
|
||||
)
|
||||
|
||||
func TestFromTestcgo(t *testing.T) {
|
||||
cltest.FromDir(t, "", "../cl/_testcgo", true)
|
||||
}
|
||||
|
||||
func TestFromTestdata(t *testing.T) {
|
||||
cltest.FromDir(t, "", "../cl/_testdata", false)
|
||||
}
|
||||
|
||||
func TestRuntime(t *testing.T) {
|
||||
cltest.Pkg(t, "github.com/goplus/llgo/internal/runtime", "../internal/runtime/llgo_autogen.ll")
|
||||
cltest.Pkg(t, "github.com/goplus/llgo/internal/abi", "../internal/abi/llgo_autogen.ll")
|
||||
}
|
||||
@@ -106,6 +106,7 @@ func (g Global) Init(v Expr) {
|
||||
// respectively, and is nil in the generic method.
|
||||
type aFunction struct {
|
||||
Expr
|
||||
pkg Package
|
||||
prog Program
|
||||
blks []BasicBlock
|
||||
|
||||
@@ -116,9 +117,9 @@ type aFunction struct {
|
||||
// Function represents a function or method.
|
||||
type Function = *aFunction
|
||||
|
||||
func newFunction(fn llvm.Value, t Type, prog Program) Function {
|
||||
func newFunction(fn llvm.Value, t Type, pkg Package, prog Program) Function {
|
||||
params, hasVArg := newParams(t, prog)
|
||||
return &aFunction{Expr{fn, t}, prog, nil, params, hasVArg}
|
||||
return &aFunction{Expr{fn, t}, pkg, prog, nil, params, hasVArg}
|
||||
}
|
||||
|
||||
func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
|
||||
@@ -145,7 +146,8 @@ func (p Function) Param(i int) Expr {
|
||||
func (p Function) NewBuilder() Builder {
|
||||
prog := p.prog
|
||||
b := prog.ctx.NewBuilder()
|
||||
b.Finalize()
|
||||
// TODO(xsw): Finalize may cause panic, so comment it.
|
||||
// b.Finalize()
|
||||
return &aBuilder{b, p, prog}
|
||||
}
|
||||
|
||||
|
||||
341
ssa/expr.go
341
ssa/expr.go
@@ -23,6 +23,7 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
@@ -34,6 +35,36 @@ type Expr struct {
|
||||
Type
|
||||
}
|
||||
|
||||
/*
|
||||
// TypeOf returns the type of the expression.
|
||||
func (v Expr) TypeOf() types.Type {
|
||||
return v.t
|
||||
}
|
||||
*/
|
||||
|
||||
// Do evaluates the delay expression and returns the result.
|
||||
func (v Expr) Do() Expr {
|
||||
if vt := v.Type; vt.kind == vkDelayExpr {
|
||||
return vt.t.(delayExprTy)()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// DelayExpr returns a delay expression.
|
||||
func DelayExpr(f func() Expr) Expr {
|
||||
return Expr{Type: &aType{t: delayExprTy(f), kind: vkDelayExpr}}
|
||||
}
|
||||
|
||||
type delayExprTy func() Expr
|
||||
|
||||
func (p delayExprTy) Underlying() types.Type {
|
||||
panic("don't call")
|
||||
}
|
||||
|
||||
func (p delayExprTy) String() string {
|
||||
return "delayExpr"
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func llvmValues(vals []Expr) []llvm.Value {
|
||||
@@ -46,10 +77,26 @@ func llvmValues(vals []Expr) []llvm.Value {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Null returns a null constant expression.
|
||||
func (p Program) Null(t Type) Expr {
|
||||
return Expr{llvm.ConstNull(t.ll), t}
|
||||
}
|
||||
|
||||
// CStringVal returns a c-style string constant expression.
|
||||
func (p Program) CStringVal(v string) Expr {
|
||||
t := p.CString()
|
||||
return Expr{llvm.ConstString(v, true), t}
|
||||
}
|
||||
|
||||
// StringVal returns string constant expression.
|
||||
func (p Program) StringVal(v string) Expr {
|
||||
t := p.String()
|
||||
cstr := llvm.ConstString(v, true)
|
||||
// TODO(xsw): cstr => gostring
|
||||
return Expr{cstr, t}
|
||||
}
|
||||
|
||||
// BoolVal returns a boolean constant expression.
|
||||
func (p Program) BoolVal(v bool) Expr {
|
||||
t := p.Bool()
|
||||
var bv uint64
|
||||
@@ -60,15 +107,19 @@ func (p Program) BoolVal(v bool) Expr {
|
||||
return Expr{ret, t}
|
||||
}
|
||||
|
||||
// IntVal returns an integer constant expression.
|
||||
func (p Program) IntVal(v uint64, t Type) Expr {
|
||||
ret := llvm.ConstInt(t.ll, v, false)
|
||||
return Expr{ret, t}
|
||||
}
|
||||
|
||||
// Val returns a constant expression.
|
||||
func (p Program) Val(v interface{}) Expr {
|
||||
switch v := v.(type) {
|
||||
case int:
|
||||
return p.IntVal(uint64(v), p.Int())
|
||||
case uintptr:
|
||||
return p.IntVal(uint64(v), p.Uintptr())
|
||||
case bool:
|
||||
return p.BoolVal(v)
|
||||
case float64:
|
||||
@@ -79,17 +130,24 @@ func (p Program) Val(v interface{}) Expr {
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
// Const returns a constant expression.
|
||||
func (b Builder) Const(v constant.Value, typ Type) Expr {
|
||||
prog := b.prog
|
||||
if v == nil {
|
||||
return prog.Null(typ)
|
||||
}
|
||||
switch t := typ.t.(type) {
|
||||
case *types.Basic:
|
||||
kind := t.Kind()
|
||||
switch {
|
||||
case kind == types.Bool:
|
||||
return b.prog.BoolVal(constant.BoolVal(v))
|
||||
return prog.BoolVal(constant.BoolVal(v))
|
||||
case kind >= types.Int && kind <= types.Uintptr:
|
||||
if v, exact := constant.Uint64Val(v); exact {
|
||||
return b.prog.IntVal(v, typ)
|
||||
return prog.IntVal(v, typ)
|
||||
}
|
||||
case kind == types.String:
|
||||
return prog.StringVal(constant.StringVal(v))
|
||||
}
|
||||
}
|
||||
panic("todo")
|
||||
@@ -225,7 +283,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
|
||||
case vkSigned:
|
||||
pred := intPredOpToLLVM[op-predOpBase]
|
||||
return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret}
|
||||
case vkUnsigned:
|
||||
case vkUnsigned, vkPtr:
|
||||
pred := uintPredOpToLLVM[op-predOpBase]
|
||||
return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret}
|
||||
case vkFloat:
|
||||
@@ -258,7 +316,7 @@ func (b Builder) UnOp(op token.Token, x Expr) Expr {
|
||||
// Load returns the value at the pointer ptr.
|
||||
func (b Builder) Load(ptr Expr) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("Load %v\n", ptr.impl.Name())
|
||||
log.Printf("Load %v\n", ptr.impl)
|
||||
}
|
||||
telem := b.prog.Elem(ptr.Type)
|
||||
return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem}
|
||||
@@ -267,12 +325,42 @@ func (b Builder) Load(ptr Expr) Expr {
|
||||
// Store stores val at the pointer ptr.
|
||||
func (b Builder) Store(ptr, val Expr) Builder {
|
||||
if debugInstr {
|
||||
log.Printf("Store %v, %v\n", ptr.impl.Name(), val.impl)
|
||||
log.Printf("Store %v, %v\n", ptr.impl, val.impl)
|
||||
}
|
||||
b.impl.CreateStore(val.impl, ptr.impl)
|
||||
return b
|
||||
}
|
||||
|
||||
// The FieldAddr instruction yields the address of Field of *struct X.
|
||||
//
|
||||
// The field is identified by its index within the field list of the
|
||||
// struct type of X.
|
||||
//
|
||||
// Dynamically, this instruction panics if X evaluates to a nil
|
||||
// pointer.
|
||||
//
|
||||
// Type() returns a (possibly named) *types.Pointer.
|
||||
//
|
||||
// Pos() returns the position of the ast.SelectorExpr.Sel for the
|
||||
// field, if explicit in the source. For implicit selections, returns
|
||||
// the position of the inducing explicit selection. If produced for a
|
||||
// struct literal S{f: e}, it returns the position of the colon; for
|
||||
// S{e} it returns the start of expression e.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = &t0.name [#1]
|
||||
func (b Builder) FieldAddr(x Expr, idx int) Expr {
|
||||
if debugInstr {
|
||||
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
|
||||
}
|
||||
prog := b.prog
|
||||
tstruc := prog.Elem(x.Type)
|
||||
telem := prog.Field(tstruc, idx)
|
||||
pt := prog.Pointer(telem)
|
||||
return Expr{llvm.CreateStructGEP(b.impl, tstruc.ll, x.impl, idx), pt}
|
||||
}
|
||||
|
||||
// The IndexAddr instruction yields the address of the element at
|
||||
// index `idx` of collection `x`. `idx` is an integer expression.
|
||||
//
|
||||
@@ -315,23 +403,240 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
|
||||
//
|
||||
// t0 = local int
|
||||
// t1 = new int
|
||||
func (b Builder) Alloc(t Type, heap bool) (ret Expr) {
|
||||
func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Alloc %v, %v\n", t.ll, heap)
|
||||
log.Printf("Alloc %v, %v\n", t, heap)
|
||||
}
|
||||
telem := b.prog.Elem(t)
|
||||
prog := b.prog
|
||||
telem := t.Elem()
|
||||
if heap {
|
||||
ret.impl = llvm.CreateAlloca(b.impl, telem.ll)
|
||||
pkg := b.fn.pkg
|
||||
size := unsafe.Sizeof(telem)
|
||||
ret = b.Call(pkg.rtFunc("Alloc"), prog.Val(size))
|
||||
} else {
|
||||
panic("todo")
|
||||
ret.impl = llvm.CreateAlloca(b.impl, prog.Type(telem).ll)
|
||||
}
|
||||
// TODO: zero-initialize
|
||||
ret.Type = t
|
||||
// TODO(xsw): zero-initialize
|
||||
ret.Type = prog.Type(t)
|
||||
return
|
||||
}
|
||||
|
||||
// The ChangeType instruction applies to X a value-preserving type
|
||||
// change to Type().
|
||||
//
|
||||
// Type changes are permitted:
|
||||
// - between a named type and its underlying type.
|
||||
// - between two named types of the same underlying type.
|
||||
// - between (possibly named) pointers to identical base types.
|
||||
// - from a bidirectional channel to a read- or write-channel,
|
||||
// optionally adding/removing a name.
|
||||
// - between a type (t) and an instance of the type (tσ), i.e.
|
||||
// Type() == σ(X.Type()) (or X.Type()== σ(Type())) where
|
||||
// σ is the type substitution of Parent().TypeParams by
|
||||
// Parent().TypeArgs.
|
||||
//
|
||||
// This operation cannot fail dynamically.
|
||||
//
|
||||
// Type changes may to be to or from a type parameter (or both). All
|
||||
// types in the type set of X.Type() have a value-preserving type
|
||||
// change to all types in the type set of Type().
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = changetype *int <- IntPtr (t0)
|
||||
func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("ChangeType %v, %v\n", t.t, x.impl)
|
||||
}
|
||||
typ := t.t
|
||||
switch typ.(type) {
|
||||
default:
|
||||
ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast")
|
||||
ret.Type = b.prog.Type(typ)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// The Convert instruction yields the conversion of value X to type
|
||||
// Type(). One or both of those types is basic (but possibly named).
|
||||
//
|
||||
// A conversion may change the value and representation of its operand.
|
||||
// Conversions are permitted:
|
||||
// - between real numeric types.
|
||||
// - between complex numeric types.
|
||||
// - between string and []byte or []rune.
|
||||
// - between pointers and unsafe.Pointer.
|
||||
// - between unsafe.Pointer and uintptr.
|
||||
// - from (Unicode) integer to (UTF-8) string.
|
||||
//
|
||||
// A conversion may imply a type name change also.
|
||||
//
|
||||
// Conversions may to be to or from a type parameter. All types in
|
||||
// the type set of X.Type() can be converted to all types in the type
|
||||
// set of Type().
|
||||
//
|
||||
// This operation cannot fail dynamically.
|
||||
//
|
||||
// Conversions of untyped string/number/bool constants to a specific
|
||||
// representation are eliminated during SSA construction.
|
||||
//
|
||||
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
|
||||
// from an explicit conversion in the source.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = convert []byte <- string (t0)
|
||||
func (b Builder) Convert(t Type, x Expr) (ret Expr) {
|
||||
typ := t.t
|
||||
ret.Type = b.prog.Type(typ)
|
||||
switch und := typ.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
kind := und.Kind()
|
||||
switch {
|
||||
case kind >= types.Int && kind <= types.Uintptr:
|
||||
ret.impl = castInt(b.impl, x.impl, t.ll)
|
||||
return
|
||||
case kind == types.UnsafePointer:
|
||||
ret.impl = castPtr(b.impl, x.impl, t.ll)
|
||||
return
|
||||
}
|
||||
case *types.Pointer:
|
||||
ret.impl = castPtr(b.impl, x.impl, t.ll)
|
||||
return
|
||||
}
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
func castInt(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
|
||||
xt := x.Type()
|
||||
if xt.TypeKind() == llvm.PointerTypeKind {
|
||||
return llvm.CreatePtrToInt(b, x, t)
|
||||
}
|
||||
if xt.IntTypeWidth() <= t.IntTypeWidth() {
|
||||
return llvm.CreateIntCast(b, x, t)
|
||||
}
|
||||
return llvm.CreateTrunc(b, x, t)
|
||||
}
|
||||
|
||||
func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
|
||||
if x.Type().TypeKind() == llvm.PointerTypeKind {
|
||||
return llvm.CreatePointerCast(b, x, t)
|
||||
}
|
||||
return llvm.CreateIntToPtr(b, x, t)
|
||||
}
|
||||
|
||||
// MakeInterface constructs an instance of an interface type from a
|
||||
// value of a concrete type.
|
||||
//
|
||||
// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set
|
||||
// of X, and Program.MethodValue(m) to find the implementation of a method.
|
||||
//
|
||||
// To construct the zero value of an interface type T, use:
|
||||
//
|
||||
// NewConst(constant.MakeNil(), T, pos)
|
||||
//
|
||||
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
|
||||
// from an explicit conversion in the source.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = make interface{} <- int (42:int)
|
||||
// t2 = make Stringer <- t0
|
||||
func (b Builder) MakeInterface(inter types.Type, x Expr, mayDelay bool) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("MakeInterface %v, %v\n", inter, x.impl)
|
||||
}
|
||||
t := inter.Underlying().(*types.Interface)
|
||||
isAny := t.Empty()
|
||||
fnDo := func() Expr {
|
||||
pkg := b.fn.pkg
|
||||
switch x.kind {
|
||||
case vkSigned, vkUnsigned, vkFloat:
|
||||
fn := pkg.rtFunc("MakeAnyInt")
|
||||
return b.InlineCall(fn, x)
|
||||
case vkString:
|
||||
fn := pkg.rtFunc("MakeAnyString")
|
||||
return b.InlineCall(fn, x)
|
||||
}
|
||||
panic("todo")
|
||||
}
|
||||
if mayDelay && isAny {
|
||||
return DelayExpr(fnDo)
|
||||
}
|
||||
return fnDo()
|
||||
}
|
||||
|
||||
// The TypeAssert instruction tests whether interface value X has type
|
||||
// AssertedType.
|
||||
//
|
||||
// If !CommaOk, on success it returns v, the result of the conversion
|
||||
// (defined below); on failure it panics.
|
||||
//
|
||||
// If CommaOk: on success it returns a pair (v, true) where v is the
|
||||
// result of the conversion; on failure it returns (z, false) where z
|
||||
// is AssertedType's zero value. The components of the pair must be
|
||||
// accessed using the Extract instruction.
|
||||
//
|
||||
// If Underlying: tests whether interface value X has the underlying
|
||||
// type AssertedType.
|
||||
//
|
||||
// If AssertedType is a concrete type, TypeAssert checks whether the
|
||||
// dynamic type in interface X is equal to it, and if so, the result
|
||||
// of the conversion is a copy of the value in the interface.
|
||||
//
|
||||
// If AssertedType is an interface, TypeAssert checks whether the
|
||||
// dynamic type of the interface is assignable to it, and if so, the
|
||||
// result of the conversion is a copy of the interface value X.
|
||||
// If AssertedType is a superinterface of X.Type(), the operation will
|
||||
// fail iff the operand is nil. (Contrast with ChangeInterface, which
|
||||
// performs no nil-check.)
|
||||
//
|
||||
// Type() reflects the actual type of the result, possibly a
|
||||
// 2-types.Tuple; AssertedType is the asserted type.
|
||||
//
|
||||
// Depending on the TypeAssert's purpose, Pos may return:
|
||||
// - the ast.CallExpr.Lparen of an explicit T(e) conversion;
|
||||
// - the ast.TypeAssertExpr.Lparen of an explicit e.(T) operation;
|
||||
// - the ast.CaseClause.Case of a case of a type-switch statement;
|
||||
// - the Ident(m).NamePos of an interface method value i.m
|
||||
// (for which TypeAssert may be used to effect the nil check).
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = typeassert t0.(int)
|
||||
// t3 = typeassert,ok t2.(T)
|
||||
func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.t, commaOk)
|
||||
}
|
||||
switch assertedTyp.kind {
|
||||
case vkSigned, vkUnsigned, vkFloat:
|
||||
pkg := b.fn.pkg
|
||||
fnName := "I2Int"
|
||||
if commaOk {
|
||||
fnName = "CheckI2Int"
|
||||
}
|
||||
fn := pkg.rtFunc(fnName)
|
||||
var kind types.BasicKind
|
||||
switch t := assertedTyp.t.(type) {
|
||||
case *types.Basic:
|
||||
kind = t.Kind()
|
||||
default:
|
||||
panic("todo")
|
||||
}
|
||||
typ := b.InlineCall(pkg.rtFunc("Basic"), b.prog.Val(int(kind)))
|
||||
return b.InlineCall(fn, x, typ)
|
||||
}
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// TODO(xsw): make inline call
|
||||
func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
|
||||
return b.Call(fn, args...)
|
||||
}
|
||||
|
||||
// The Call instruction represents a function or method call.
|
||||
//
|
||||
// The Call instruction yields the function result if there is exactly
|
||||
@@ -346,7 +651,7 @@ func (b Builder) Alloc(t Type, heap bool) (ret Expr) {
|
||||
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
|
||||
if debugInstr {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprint(&b, "Call @", fn.impl.Name())
|
||||
fmt.Fprint(&b, "Call ", fn.impl.Name())
|
||||
for _, arg := range args {
|
||||
fmt.Fprint(&b, ", ", arg.impl)
|
||||
}
|
||||
@@ -362,4 +667,14 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
|
||||
return
|
||||
}
|
||||
|
||||
// A Builtin represents a specific use of a built-in function, e.g. len.
|
||||
//
|
||||
// Builtins are immutable values. Builtins do not have addresses.
|
||||
//
|
||||
// `fn` indicates the function: one of the built-in functions from the
|
||||
// Go spec (excluding "make" and "new").
|
||||
func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
135
ssa/package.go
135
ssa/package.go
@@ -24,6 +24,10 @@ import (
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
const (
|
||||
PkgRuntime = "github.com/goplus/llgo/internal/runtime"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type dbgFlags = int
|
||||
@@ -94,6 +98,9 @@ type aProgram struct {
|
||||
ctx llvm.Context
|
||||
typs typeutil.Map
|
||||
|
||||
rt *types.Package
|
||||
rtget func() *types.Package
|
||||
|
||||
target *Target
|
||||
td llvm.TargetData
|
||||
// tm llvm.TargetMachine
|
||||
@@ -107,10 +114,20 @@ type aProgram struct {
|
||||
voidType llvm.Type
|
||||
voidPtrTy llvm.Type
|
||||
|
||||
voidTy Type
|
||||
boolTy Type
|
||||
intTy Type
|
||||
f64Ty Type
|
||||
rtStringTy llvm.Type
|
||||
rtIfaceTy llvm.Type
|
||||
rtSliceTy llvm.Type
|
||||
|
||||
anyTy Type
|
||||
voidTy Type
|
||||
boolTy Type
|
||||
cstrTy Type
|
||||
stringTy Type
|
||||
uintptrTy Type
|
||||
intTy Type
|
||||
f64Ty Type
|
||||
|
||||
needRuntime bool
|
||||
}
|
||||
|
||||
// A Program presents a program.
|
||||
@@ -122,17 +139,73 @@ func NewProgram(target *Target) Program {
|
||||
target = &Target{}
|
||||
}
|
||||
ctx := llvm.NewContext()
|
||||
ctx.Finalize()
|
||||
// TODO(xsw): Finalize may cause panic, so comment it.
|
||||
// ctx.Finalize()
|
||||
td := llvm.NewTargetData("") // TODO(xsw): target config
|
||||
return &aProgram{ctx: ctx, target: target, td: td}
|
||||
}
|
||||
|
||||
// SetRuntime sets the runtime.
|
||||
// Its type can be *types.Package or func() *types.Package.
|
||||
func (p Program) SetRuntime(runtime any) {
|
||||
switch v := runtime.(type) {
|
||||
case *types.Package:
|
||||
p.rt = v
|
||||
case func() *types.Package:
|
||||
p.rtget = v
|
||||
}
|
||||
}
|
||||
|
||||
// NeedRuntime returns if the current package needs runtime.
|
||||
func (p Program) NeedRuntime() bool {
|
||||
return p.needRuntime
|
||||
}
|
||||
|
||||
func (p Program) runtime() *types.Package {
|
||||
if p.rt == nil {
|
||||
p.rt = p.rtget()
|
||||
}
|
||||
p.needRuntime = true
|
||||
return p.rt
|
||||
}
|
||||
|
||||
func (p Program) rtNamed(name string) *types.Named {
|
||||
return p.runtime().Scope().Lookup(name).Type().(*types.Named)
|
||||
}
|
||||
|
||||
func (p Program) rtType(name string) Type {
|
||||
return p.Type(p.rtNamed(name))
|
||||
}
|
||||
|
||||
func (p Program) rtIface() llvm.Type {
|
||||
if p.rtIfaceTy.IsNil() {
|
||||
p.rtIfaceTy = p.rtType("Interface").ll
|
||||
}
|
||||
return p.rtIfaceTy
|
||||
}
|
||||
|
||||
func (p Program) rtSlice() llvm.Type {
|
||||
if p.rtSliceTy.IsNil() {
|
||||
p.rtSliceTy = p.rtType("Slice").ll
|
||||
}
|
||||
return p.rtSliceTy
|
||||
}
|
||||
|
||||
func (p Program) rtString() llvm.Type {
|
||||
if p.rtStringTy.IsNil() {
|
||||
p.rtStringTy = p.rtType("String").ll
|
||||
}
|
||||
return p.rtStringTy
|
||||
}
|
||||
|
||||
// NewPackage creates a new package.
|
||||
func (p Program) NewPackage(name, pkgPath string) Package {
|
||||
mod := p.ctx.NewModule(pkgPath)
|
||||
mod.Finalize()
|
||||
// TODO(xsw): Finalize may cause panic, so comment it.
|
||||
// mod.Finalize()
|
||||
fns := make(map[string]Function)
|
||||
gbls := make(map[string]Global)
|
||||
p.needRuntime = false
|
||||
return &aPackage{mod, fns, gbls, p}
|
||||
}
|
||||
|
||||
@@ -152,6 +225,28 @@ func (p Program) Bool() Type {
|
||||
return p.boolTy
|
||||
}
|
||||
|
||||
func (p Program) CString() Type {
|
||||
if p.cstrTy == nil { // *int8
|
||||
p.cstrTy = p.Type(types.NewPointer(types.Typ[types.Int8]))
|
||||
}
|
||||
return p.cstrTy
|
||||
}
|
||||
|
||||
func (p Program) String() Type {
|
||||
if p.stringTy == nil {
|
||||
p.stringTy = p.Type(types.Typ[types.String])
|
||||
}
|
||||
return p.stringTy
|
||||
}
|
||||
|
||||
// Any returns any type.
|
||||
func (p Program) Any() Type {
|
||||
if p.anyTy == nil {
|
||||
p.anyTy = p.Type(tyAny)
|
||||
}
|
||||
return p.anyTy
|
||||
}
|
||||
|
||||
// Int returns int type.
|
||||
func (p Program) Int() Type {
|
||||
if p.intTy == nil {
|
||||
@@ -160,6 +255,14 @@ func (p Program) Int() Type {
|
||||
return p.intTy
|
||||
}
|
||||
|
||||
// Uintptr returns uintptr type.
|
||||
func (p Program) Uintptr() Type {
|
||||
if p.uintptrTy == nil {
|
||||
p.uintptrTy = p.Type(types.Typ[types.Uintptr])
|
||||
}
|
||||
return p.uintptrTy
|
||||
}
|
||||
|
||||
// Float64 returns float64 type.
|
||||
func (p Program) Float64() Type {
|
||||
if p.f64Ty == nil {
|
||||
@@ -187,6 +290,7 @@ type aPackage struct {
|
||||
|
||||
type Package = *aPackage
|
||||
|
||||
// NewConst creates a new named constant.
|
||||
func (p Package) NewConst(name string, val constant.Value) NamedConst {
|
||||
return &aNamedConst{}
|
||||
}
|
||||
@@ -200,11 +304,19 @@ func (p Package) NewVar(name string, typ types.Type) Global {
|
||||
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) Function {
|
||||
t := p.prog.llvmSignature(sig)
|
||||
if v, ok := p.fns[name]; ok {
|
||||
return v
|
||||
}
|
||||
t := p.prog.llvmSignature(sig, false)
|
||||
fn := llvm.AddFunction(p.mod, name, t.ll)
|
||||
ret := newFunction(fn, t, p.prog)
|
||||
ret := newFunction(fn, t, p, p.prog)
|
||||
p.fns[name] = ret
|
||||
return ret
|
||||
}
|
||||
@@ -214,9 +326,10 @@ func (p Package) FuncOf(name string) Function {
|
||||
return p.fns[name]
|
||||
}
|
||||
|
||||
// VarOf returns a global variable by name.
|
||||
func (p Package) VarOf(name string) Global {
|
||||
return p.vars[name]
|
||||
func (p Package) rtFunc(fnName string) Expr {
|
||||
fn := p.prog.runtime().Scope().Lookup(fnName).(*types.Func)
|
||||
name := FullName(fn.Pkg(), fnName)
|
||||
return p.NewFunc(name, fn.Type().(*types.Signature)).Expr
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -23,9 +23,35 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Initialize(InitAll)
|
||||
SetDebug(DbgFlagAll)
|
||||
/*
|
||||
func TestMakeInterface(t *testing.T) {
|
||||
var b Builder
|
||||
b.MakeInterface(types.NewInterfaceType(nil, nil), Expr{}, true).Do(true)
|
||||
}
|
||||
*/
|
||||
|
||||
func TestDelayExpr(t *testing.T) {
|
||||
a := delayExprTy(nil)
|
||||
_ = a.String()
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Log("TestDelayExpr: no error?")
|
||||
}
|
||||
}()
|
||||
a.Underlying()
|
||||
}
|
||||
|
||||
func TestAny(t *testing.T) {
|
||||
prog := NewProgram(nil)
|
||||
prog.SetRuntime(func() *types.Package {
|
||||
ret := types.NewPackage("runtime", "runtime")
|
||||
scope := ret.Scope()
|
||||
name := types.NewTypeName(0, ret, "Interface", nil)
|
||||
types.NewNamed(name, types.NewStruct(nil, nil), nil)
|
||||
scope.Insert(name)
|
||||
return ret
|
||||
})
|
||||
prog.Any()
|
||||
}
|
||||
|
||||
func assertPkg(t *testing.T, p Package, expected string) {
|
||||
@@ -78,6 +104,9 @@ source_filename = "foo/bar"
|
||||
|
||||
@a = external global {}
|
||||
`)
|
||||
if prog.NeedRuntime() {
|
||||
t.Fatal("NeedRuntime?")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamedStruct(t *testing.T) {
|
||||
@@ -93,9 +122,9 @@ func TestNamedStruct(t *testing.T) {
|
||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||
source_filename = "foo/bar"
|
||||
|
||||
%Empty = type {}
|
||||
%bar.Empty = type {}
|
||||
|
||||
@a = external global %Empty
|
||||
@a = external global %bar.Empty
|
||||
`)
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,14 @@ func (b Builder) SetBlock(blk BasicBlock) Builder {
|
||||
return b
|
||||
}
|
||||
|
||||
// Panic emits a panic instruction.
|
||||
func (b Builder) Panic(v Expr) {
|
||||
if debugInstr {
|
||||
log.Printf("Panic %v\n", v.impl)
|
||||
}
|
||||
b.impl.CreateUnreachable() // TODO(xsw): pass v
|
||||
}
|
||||
|
||||
// Return emits a return instruction.
|
||||
func (b Builder) Return(results ...Expr) {
|
||||
if debugInstr {
|
||||
|
||||
80
ssa/type.go
80
ssa/type.go
@@ -17,12 +17,16 @@
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"log"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
)
|
||||
|
||||
var (
|
||||
tyAny = types.NewInterfaceType(nil, nil)
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type valueKind = int
|
||||
@@ -35,8 +39,10 @@ const (
|
||||
vkComplex
|
||||
vkString
|
||||
vkBool
|
||||
vkPtr
|
||||
vkFunc
|
||||
vkTuple
|
||||
vkDelayExpr = -1
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -72,6 +78,22 @@ func indexType(t types.Type) types.Type {
|
||||
panic("index: type doesn't support index - " + t.String())
|
||||
}
|
||||
|
||||
// convert method to func
|
||||
func methodToFunc(sig *types.Signature) *types.Signature {
|
||||
if recv := sig.Recv(); recv != nil {
|
||||
tParams := sig.Params()
|
||||
nParams := tParams.Len()
|
||||
params := make([]*types.Var, nParams+1)
|
||||
params[0] = recv
|
||||
for i := 0; i < nParams; i++ {
|
||||
params[i+1] = tParams.At(i)
|
||||
}
|
||||
return types.NewSignatureType(
|
||||
nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic())
|
||||
}
|
||||
return sig
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type aType struct {
|
||||
@@ -95,7 +117,15 @@ func (p Program) Index(typ Type) Type {
|
||||
return p.Type(indexType(typ.t))
|
||||
}
|
||||
|
||||
func (p Program) Field(typ Type, i int) Type {
|
||||
tunder := typ.t.Underlying()
|
||||
return p.Type(tunder.(*types.Struct).Field(i).Type())
|
||||
}
|
||||
|
||||
func (p Program) Type(typ types.Type) Type {
|
||||
if sig, ok := typ.(*types.Signature); ok { // should methodToFunc
|
||||
return p.llvmSignature(sig, true)
|
||||
}
|
||||
if v := p.typs.At(typ); v != nil {
|
||||
return v.(Type)
|
||||
}
|
||||
@@ -104,11 +134,12 @@ func (p Program) Type(typ types.Type) Type {
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p Program) llvmSignature(sig *types.Signature) Type {
|
||||
func (p Program) llvmSignature(sig *types.Signature, isPtr bool) Type {
|
||||
sig = methodToFunc(sig)
|
||||
if v := p.typs.At(sig); v != nil {
|
||||
return v.(Type)
|
||||
}
|
||||
ret := p.toLLVMFunc(sig)
|
||||
ret := p.toLLVMFunc(sig, isPtr)
|
||||
p.typs.Set(sig, ret)
|
||||
return ret
|
||||
}
|
||||
@@ -209,27 +240,28 @@ func (p Program) toLLVMType(typ types.Type) Type {
|
||||
case types.Complex64:
|
||||
case types.Complex128:
|
||||
case types.String:
|
||||
return &aType{p.rtString(), typ, vkString}
|
||||
case types.UnsafePointer:
|
||||
return &aType{p.tyVoidPtr(), typ, vkInvalid}
|
||||
return &aType{p.tyVoidPtr(), typ, vkPtr}
|
||||
}
|
||||
case *types.Pointer:
|
||||
elem := p.Type(t.Elem())
|
||||
return &aType{llvm.PointerType(elem.ll, 0), typ, vkInvalid}
|
||||
return &aType{llvm.PointerType(elem.ll, 0), typ, vkPtr}
|
||||
case *types.Interface:
|
||||
return &aType{p.rtIface(), typ, vkInvalid}
|
||||
case *types.Slice:
|
||||
return &aType{p.rtSlice(), typ, vkInvalid}
|
||||
case *types.Map:
|
||||
case *types.Struct:
|
||||
return p.toLLVMStruct(t)
|
||||
case *types.Named:
|
||||
return p.toLLVMNamed(t)
|
||||
case *types.Signature:
|
||||
return p.toLLVMFunc(t)
|
||||
case *types.Array:
|
||||
elem := p.Type(t.Elem())
|
||||
return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid}
|
||||
case *types.Chan:
|
||||
}
|
||||
log.Println("toLLVMType: todo -", typ)
|
||||
panic("todo")
|
||||
panic(fmt.Sprintf("toLLVMType: todo - %T\n", typ))
|
||||
}
|
||||
|
||||
func (p Program) toLLVMNamedStruct(name string, typ *types.Struct) llvm.Type {
|
||||
@@ -269,7 +301,7 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) {
|
||||
return
|
||||
}
|
||||
|
||||
func (p Program) toLLVMFunc(sig *types.Signature) Type {
|
||||
func (p Program) toLLVMFunc(sig *types.Signature, isPtr bool) Type {
|
||||
tParams := sig.Params()
|
||||
n := tParams.Len()
|
||||
hasVArg := HasVArg(tParams, n)
|
||||
@@ -288,6 +320,9 @@ func (p Program) toLLVMFunc(sig *types.Signature) Type {
|
||||
ret = p.toLLVMTuple(out)
|
||||
}
|
||||
ft := llvm.FunctionType(ret, params, hasVArg)
|
||||
if isPtr {
|
||||
ft = llvm.PointerType(ft, 0)
|
||||
}
|
||||
return &aType{ft, sig, vkFunc}
|
||||
}
|
||||
|
||||
@@ -304,12 +339,29 @@ func (p Program) retType(sig *types.Signature) Type {
|
||||
}
|
||||
|
||||
func (p Program) toLLVMNamed(typ *types.Named) Type {
|
||||
name := typ.Obj().Name()
|
||||
switch typ := typ.Underlying().(type) {
|
||||
switch t := typ.Underlying().(type) {
|
||||
case *types.Struct:
|
||||
return &aType{p.toLLVMNamedStruct(name, typ), typ, vkInvalid}
|
||||
name := NameOf(typ)
|
||||
return &aType{p.toLLVMNamedStruct(name, t), typ, vkInvalid}
|
||||
default:
|
||||
return p.Type(t)
|
||||
}
|
||||
panic("todo")
|
||||
}
|
||||
|
||||
func NameOf(typ *types.Named) string {
|
||||
obj := typ.Obj()
|
||||
return FullName(obj.Pkg(), obj.Name())
|
||||
}
|
||||
|
||||
func FullName(pkg *types.Package, name string) string {
|
||||
return PathOf(pkg) + "." + name
|
||||
}
|
||||
|
||||
func PathOf(pkg *types.Package) string {
|
||||
if pkg.Name() == "main" {
|
||||
return "main"
|
||||
}
|
||||
return pkg.Path()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package clang
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
@@ -26,6 +27,9 @@ import (
|
||||
// Cmd represents a nm command.
|
||||
type Cmd struct {
|
||||
app string
|
||||
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
}
|
||||
|
||||
// New creates a new nm command.
|
||||
@@ -33,13 +37,13 @@ func New(app string) *Cmd {
|
||||
if app == "" {
|
||||
app = "clang"
|
||||
}
|
||||
return &Cmd{app}
|
||||
return &Cmd{app, os.Stdout, os.Stderr}
|
||||
}
|
||||
|
||||
func (p *Cmd) Exec(args ...string) error {
|
||||
cmd := exec.Command(p.app, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = p.Stdout
|
||||
cmd.Stderr = p.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user