Compare commits

..

75 Commits

Author SHA1 Message Date
xushiwei
695d3f3327 Merge pull request #73 from xushiwei/q
llvm v0.7.2
2024-04-29 03:50:05 +08:00
xushiwei
7979cfcb06 update llgo_autogen.ll 2024-04-29 03:45:52 +08:00
xushiwei
2986cb0c5f llvm v0.7.2 2024-04-29 03:43:38 +08:00
xushiwei
5a175955a9 Merge pull request #72 from xushiwei/q
llgo/ssa: use b.CreateUnreachable to panic
2024-04-29 03:40:05 +08:00
xushiwei
cb507f43a7 llgo/ssa: use b.CreateUnreachable to panic 2024-04-29 03:36:19 +08:00
xushiwei
8b148d72c2 Merge pull request #71 from xushiwei/q
cl: don't need to compile alias type
2024-04-29 02:58:22 +08:00
xushiwei
616596e571 cl: don't need to compile alias type 2024-04-29 02:56:21 +08:00
xushiwei
2849fe4841 Merge pull request #70 from xushiwei/q
llgo/ssa: llvmSignature/castPtr/castInt bugfix; link: runtime
2024-04-29 01:44:01 +08:00
xushiwei
98065e80d0 TestRuntime 2024-04-29 01:42:00 +08:00
xushiwei
286b520d83 build: better error messages 2024-04-29 01:34:21 +08:00
xushiwei
769b93a277 build: mark need runtime 2024-04-29 00:49:17 +08:00
xushiwei
c30ed1b3c8 llgo/ssa: llvmSignature/castPtr/castInt bugfix; link: runtime 2024-04-29 00:16:00 +08:00
xushiwei
78d7f984d1 Merge pull request #69 from xushiwei/q
llgen: smart fname of outFile (llgo_autogen.ll or out.ll)
2024-04-28 23:12:15 +08:00
xushiwei
e88f7e6659 llgen: smart fname of outFile (llgo_autogen.ll or out.ll) 2024-04-28 23:07:59 +08:00
xushiwei
e8ff879943 Merge pull request #68 from xushiwei/q
cl: compileInstrOrValue bugfix
2024-04-28 22:40:36 +08:00
xushiwei
f09d5bd155 TestIsAny, TestIntVal 2024-04-28 22:38:04 +08:00
xushiwei
53e73fc622 x 2024-04-28 22:24:03 +08:00
xushiwei
ba94d6f04e cl: compileInstrOrValue bugfix 2024-04-28 22:20:46 +08:00
xushiwei
449f91ab14 Merge pull request #67 from xushiwei/q
llgo/ssa.SetRuntime: prevent multiple loading of runtime
2024-04-28 14:02:54 +08:00
xushiwei
70623dd554 llgo/ssa.SetRuntime: prevent multiple loading of runtime 2024-04-28 14:00:29 +08:00
xushiwei
35dc6dcd85 Merge pull request #66 from xushiwei/q
runtime
2024-04-28 12:24:59 +08:00
xushiwei
41dfafe957 TestRuntime 2024-04-28 12:22:56 +08:00
xushiwei
2cc1bdee19 llgo/ssa: pkg.NewFunc bugfix 2024-04-28 12:09:47 +08:00
xushiwei
eb4146d80d llgo/ssa: Alloc bugfix (heap) 2024-04-28 10:39:19 +08:00
xushiwei
0d68066086 runtime: MakeAnyString 2024-04-28 10:29:06 +08:00
xushiwei
7039cb3bc2 llgo/ssa: support string/cstring; panic 2024-04-28 09:55:54 +08:00
xushiwei
510f2f4769 runtime: Alloc 2024-04-28 07:08:33 +08:00
xushiwei
5415f68c1b llgo/ssa: Alloc, BinOp(vkPtr) 2024-04-28 07:08:01 +08:00
xushiwei
475f0fa2ff llgo/ssa: ChangeType, Convert 2024-04-28 06:23:21 +08:00
xushiwei
c58b1140d8 Merge pull request #65 from xushiwei/q
llgo build/install/run: link runtime
2024-04-28 06:21:57 +08:00
xushiwei
c97c1e97b9 llgo build/install/run: link runtime 2024-04-27 22:13:40 +08:00
xushiwei
ba3d82e5e5 Merge pull request #64 from xushiwei/q
cl: _testcgo/any
2024-04-27 21:35:17 +08:00
xushiwei
d432899b42 TestAny, TestDelayExpr 2024-04-27 21:32:48 +08:00
xushiwei
08da38a609 cl: _testcgo/any 2024-04-27 20:45:55 +08:00
xushiwei
6a3eb2f2f9 ssa: rtIface, rtSlice 2024-04-27 18:13:16 +08:00
xushiwei
6a02c3ac4c llgo/ssa: rtType, rtFunc 2024-04-27 17:39:25 +08:00
xushiwei
f1761c0c9c llgo/internal/runtime 2024-04-27 13:57:21 +08:00
xushiwei
39076c75cf Merge pull request #63 from xushiwei/q
cl: _testcgo/typalias
2024-04-27 10:10:16 +08:00
xushiwei
3be8cacc24 cl: _testcgo/typalias 2024-04-27 10:06:59 +08:00
xushiwei
46a9df47e4 cl: _testcgo/typalias 2024-04-27 08:33:49 +08:00
xushiwei
d44a31cc62 Merge pull request #62 from xushiwei/q
cl: _testcgo/struct
2024-04-27 08:21:00 +08:00
xushiwei
00b2fd1479 fix TestNamedStruct 2024-04-27 08:17:46 +08:00
xushiwei
5f08e7a612 cl: _testcgo/struct 2024-04-27 07:47:10 +08:00
xushiwei
5c8725373a Merge pull request #61 from xushiwei/q
llgo build/install/run: ParseArgs bugfix
2024-04-27 06:43:11 +08:00
xushiwei
c455f6e730 SkipFlagArgs 2024-04-27 06:41:24 +08:00
xushiwei
3cc83b8ec4 llgo build/install/run: SkipArgs bugfix 2024-04-27 06:39:09 +08:00
xushiwei
f74de76d70 Merge pull request #60 from xushiwei/q
llgo build bugfix: when len(initial)==1
2024-04-26 20:30:22 +08:00
xushiwei
ea8ddc6451 llgo build bugfix: when len(initial)==1 2024-04-26 20:28:06 +08:00
xushiwei
5f36c37cf2 Merge pull request #59 from xushiwei/q
llgo clean
2024-04-26 20:10:15 +08:00
xushiwei
2ad2873278 llgo clean 2024-04-26 20:06:54 +08:00
xushiwei
3855895808 Merge pull request #58 from xushiwei/q
cl: go:linkname specifies call convention by C.xxx
2024-04-26 13:27:55 +08:00
xushiwei
f86cd74a98 cl: go:linkname specifies call convention by C.xxx 2024-04-26 13:09:24 +08:00
xushiwei
1e6ecbadcd Merge pull request #57 from xushiwei/q
cl: fake libc
2024-04-26 05:53:19 +08:00
xushiwei
33716a3385 cl: fake libc 2024-04-26 05:51:30 +08:00
xushiwei
252f0bf967 Merge pull request #56 from xushiwei/q
cl: TestImport/TestVarOf
2024-04-26 05:40:50 +08:00
xushiwei
2c3e1d1055 cl: TestImport/TestVarOf 2024-04-26 05:39:15 +08:00
xushiwei
aafa639bf1 Merge pull request #55 from xushiwei/q
llgo: use -Wno-override-module to disable clang warning
2024-04-26 05:09:25 +08:00
xushiwei
815a8a74fc llgo: use -Wno-override-module to disable clang warning 2024-04-26 05:05:59 +08:00
xushiwei
840ea80e20 Merge pull request #54 from xushiwei/q
llgo run: strlen
2024-04-26 04:47:24 +08:00
xushiwei
773ae2c8c6 llgo run: strlen 2024-04-26 04:44:49 +08:00
xushiwei
4aadb4b86f Merge pull request #53 from xushiwei/q
cl: _testcgo/strlen
2024-04-26 03:28:08 +08:00
xushiwei
91d1d71f6d ssa: temp disable Finalize; cl: decls sort by name 2024-04-26 03:25:11 +08:00
xushiwei
a3d6a94600 cl: _testcgo/strlen 2024-04-26 02:40:36 +08:00
xushiwei
43ae7a23b2 cl: _testcgo/strlen - todo 2024-04-26 02:05:49 +08:00
xushiwei
b6005886fa Merge pull request #52 from xushiwei/q
cl: _testdata/ptrmthd
2024-04-26 00:34:59 +08:00
xushiwei
28dd34a136 cl: _testdata/ptrmthd 2024-04-26 00:31:02 +08:00
xushiwei
3a3c263203 Merge pull request #51 from xushiwei/q
cl: _testdata/method
2024-04-25 21:47:36 +08:00
xushiwei
87b7ecd1d6 cl: _testdata/method 2024-04-25 21:44:23 +08:00
xushiwei
f5a309b5ad Merge pull request #50 from xushiwei/q
cl: _testdata/printval
2024-04-25 14:50:30 +08:00
xushiwei
0f00add402 cl: pathOf(pkg) 2024-04-25 14:30:02 +08:00
xushiwei
1014fa53dd cl: _testdata/printval 2024-04-25 14:25:14 +08:00
xushiwei
04568835bd Merge pull request #49 from xushiwei/q
codecov
2024-04-25 07:39:44 +08:00
xushiwei
e4f8edc07c codecov 2024-04-25 07:37:04 +08:00
xushiwei
a8533d1677 Merge pull request #48 from xushiwei/q
llgo/cl/cltest
2024-04-25 07:31:31 +08:00
xushiwei
edcb66afb7 llgo/cl/cltest 2024-04-25 07:29:45 +08:00
61 changed files with 3620 additions and 349 deletions

View File

@@ -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
View File

@@ -8,6 +8,10 @@
*.so
*.dylib
err.log
_go/
_runtime/
_tinygo/
# Test binary, built with `go test -c`

View File

@@ -4,7 +4,8 @@ llgo - A Go compiler based on LLVM
[![Build Status](https://github.com/goplus/llgo/actions/workflows/go.yml/badge.svg)](https://github.com/goplus/llgo/actions/workflows/go.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/goplus/llgo)](https://goreportcard.com/report/github.com/goplus/llgo)
[![GitHub release](https://img.shields.io/github/v/tag/goplus/llgo.svg?label=release)](https://github.com/goplus/llgo/releases)
[![GoDoc](https://pkg.go.dev/badge/github.com/goplus/llgo.svg)](https://pkg.go.dev/github.com/goplus/llgo)
<!--
[![Coverage Status](https://codecov.io/gh/goplus/llgo/branch/main/graph/badge.svg)](https://codecov.io/gh/goplus/llgo)
-->
[![GoDoc](https://pkg.go.dev/badge/github.com/goplus/llgo.svg)](https://pkg.go.dev/github.com/goplus/llgo)
[![Language](https://img.shields.io/badge/language-Go+-blue.svg)](https://github.com/goplus/gop)
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).

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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, ...)

View 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)
}

View 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)

View File

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

View File

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

View File

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

View 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, ...)

View File

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

View File

@@ -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, ...)

View 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)
}

View 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, ...)

View 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)
}

View 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, ...)

View File

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

View File

@@ -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
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) {
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)
}
fn := p.compileValue(b, call.Value)
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...)
}
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:

View File

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

View File

@@ -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 {
name := funcName(pkg, fn)
if v, ok := p.link[name]; ok {
return v
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"))
}
return name
func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, bool) {
name := funcName(pkg, fn)
if ignore && ignoreName(name) || checkCgo(fn.Name()) {
return name, false
}
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
View 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

View File

@@ -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 {

View File

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

View File

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

View 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)
}

View File

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

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View 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
View 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))
}
// -----------------------------------------------------------------------------

View File

@@ -19,7 +19,7 @@ package build
import (
"fmt"
"go/token"
"log"
"go/types"
"os"
"os/exec"
"path"
@@ -51,7 +51,8 @@ func needLLFile(mode Mode) bool {
type Config struct {
BinPath string
AppExt string // ".exe" on Windows, empty on Unix
RunArgs []string
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
View 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
View 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)
}

View 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"()

View 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
View 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
View 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)
}

View 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
}

View 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}
}

View 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}
}

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

View File

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

View File

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

View File

@@ -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
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
}
// -----------------------------------------------------------------------------

View File

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

View File

@@ -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 {

View File

@@ -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()
}
// -----------------------------------------------------------------------------

View File

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