Compare commits

..

52 Commits

Author SHA1 Message Date
xushiwei
028c6cdf50 Update README.md 2024-05-06 23:13:20 +08:00
xushiwei
b08ae1dff3 Merge pull request #120 from visualfc/phis
cl: fix compilePhis
2024-05-06 23:09:48 +08:00
xushiwei
37e7d66343 Merge pull request #121 from xushiwei/q
demos: hello, concat
2024-05-06 23:03:32 +08:00
xushiwei
e6d06cc278 demos: hello, concat 2024-05-06 22:31:33 +08:00
visualfc
017fd150cd cl: fix compilePhis 2024-05-06 22:23:34 +08:00
xushiwei
ea7e1de833 Merge pull request #119 from visualfc/result
result: checkExpr funcPtr => closure
2024-05-06 20:58:18 +08:00
visualfc
c2767be178 result: checkExpr funcPtr => closure 2024-05-06 19:42:18 +08:00
xushiwei
c60be43ac6 Merge pull request #118 from xushiwei/q
cl: compileFunction if not exists
2024-05-06 18:38:41 +08:00
xushiwei
c373a5b505 compileFunction bugfix: compileFuncDecl may return nil 2024-05-06 18:36:36 +08:00
xushiwei
da20aea408 cl: compileFunction if not exists 2024-05-06 18:30:53 +08:00
xushiwei
973b5b90dc Merge pull request #117 from xushiwei/q
closureStub: SetTailCall
2024-05-06 16:42:49 +08:00
xushiwei
cffb5e9539 closureStub: SetTailCall 2024-05-06 16:40:52 +08:00
xushiwei
4b35586566 Merge pull request #116 from xushiwei/q
Store: checkExpr funcPtr => closure
2024-05-06 16:13:00 +08:00
xushiwei
1e075d4830 closure 2024-05-06 16:11:14 +08:00
xushiwei
c81b5b5df9 Store: checkExpr funcPtr => closure 2024-05-06 16:10:26 +08:00
xushiwei
80b2b8d061 Merge pull request #114 from xushiwei/q
demo: qsort
2024-05-06 13:31:46 +08:00
xushiwei
d1a7f63797 demo: qsort 2024-05-06 13:26:09 +08:00
xushiwei
92359dd03b Update README.md 2024-05-06 12:36:26 +08:00
xushiwei
d1430c9f5a Update README.md 2024-05-06 12:35:38 +08:00
xushiwei
7feca4478e Merge pull request #113 from xushiwei/q
demo: genints
2024-05-06 12:33:30 +08:00
xushiwei
02b1d5ed84 README 2024-05-06 12:31:55 +08:00
xushiwei
99d74ce4b6 demo: genints 2024-05-06 12:06:12 +08:00
xushiwei
355094c7e2 Merge pull request #112 from xushiwei/q
ssa: Lookup
2024-05-06 08:22:11 +08:00
xushiwei
7f61989869 fix warning 2024-05-06 08:21:47 +08:00
xushiwei
7223ff004a chore: clangpp, clangast 2024-05-06 08:11:54 +08:00
xushiwei
df333fb144 ssa: Lookup 2024-05-06 01:17:37 +08:00
xushiwei
5c08c55957 Merge pull request #111 from xushiwei/q
closureStub: SetLinkage linkonce
2024-05-06 00:20:27 +08:00
xushiwei
26b812a62a closureStub: SetLinkage linkonce 2024-05-06 00:17:39 +08:00
xushiwei
cd3d9c709f Merge pull request #110 from xushiwei/q
closure: MakeClosure/makeClosureCtx fix
2024-05-05 23:41:51 +08:00
xushiwei
9da90e7ecf TestClosureCtx 2024-05-05 23:39:55 +08:00
xushiwei
f17a4ca1de closure: MakeClosure/makeClosureCtx fix 2024-05-05 23:32:54 +08:00
xushiwei
8ab662b373 compileValue ssa.Function fix: v.Pkg == nil: means auto generated function? 2024-05-05 22:15:39 +08:00
xushiwei
fb839da81e Merge pull request #109 from xushiwei/q
closure
2024-05-05 21:41:17 +08:00
xushiwei
244da4b10a disable TestErrCompileValue 2024-05-05 21:38:58 +08:00
xushiwei
c0c5271172 llgen runtime 2024-05-05 21:35:26 +08:00
xushiwei
0066f8bd3f llgen tests 2024-05-05 21:33:04 +08:00
xushiwei
be9d209622 compileFunc: prevent compiling multiple times 2024-05-05 21:27:22 +08:00
xushiwei
2bbd828f3a closureStub bugfix; llgen: SetRuntime 2024-05-05 19:44:16 +08:00
xushiwei
4f1b6e95a1 closureStub 2024-05-05 18:48:09 +08:00
xushiwei
87ca3a39dc cvtClosure, llvmParamsEx 2024-05-05 18:20:51 +08:00
xushiwei
d7df46d578 MakeClosure, FreeVar; FuncAddCtx; aggregateAlloc 2024-05-05 17:39:17 +08:00
xushiwei
489cbc4782 Merge pull request #108 from xushiwei/q
vkFuncDecl, vkFuncPtr, vkClosure; callback example
2024-05-05 16:03:36 +08:00
xushiwei
3c33a1d05e TestFromTestrt: callback example 2024-05-05 16:01:07 +08:00
xushiwei
067cf0cba6 vkFuncDecl, vkFuncPtr, vkClosure; callback example 2024-05-05 15:59:33 +08:00
xushiwei
03a194a514 llvmValues 2024-05-05 15:07:10 +08:00
xushiwei
bdf45c0fcb Merge pull request #107 from xushiwei/q
llgo/ssa: introduce rawType; MakeClosure
2024-05-05 14:00:51 +08:00
xushiwei
6f679c05a3 TestCvtType 2024-05-05 13:56:24 +08:00
xushiwei
0b131bd957 cl: gotypes 2024-05-05 13:49:08 +08:00
xushiwei
ebf4c80aff check types.Struct isClosure 2024-05-05 13:29:20 +08:00
xushiwei
18a63e226a Merge remote-tracking branch 'gop/main' into q 2024-05-05 12:33:35 +08:00
xushiwei
5d1d51dd58 llgo/ssa: introduce rawType 2024-05-05 12:11:51 +08:00
xushiwei
52018cd424 to MakeClosure 2024-05-04 19:44:52 +08:00
57 changed files with 4062 additions and 571 deletions

View File

@@ -9,3 +9,41 @@ llgo - A Go compiler based on LLVM
[![Language](https://img.shields.io/badge/language-Go+-blue.svg)](https://github.com/goplus/gop) [![Language](https://img.shields.io/badge/language-Go+-blue.svg)](https://github.com/goplus/gop)
This is a Go compiler based on LLVM in order to better integrate Go with the C ecosystem. It's a subproject of [the Go+ project](https://github.com/goplus/gop). This is a Go compiler based on LLVM in order to better integrate Go with the C ecosystem. It's a subproject of [the Go+ project](https://github.com/goplus/gop).
## How to install
Follow these steps to generate the `llgo` command (its usage is the same as the `go` command):
### on macOS
```sh
brew update # execute if needed
brew install llvm@17
go install -v ./...
```
### on Linux
```sh
echo 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-17 main' | sudo tee /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update # execute if needed
sudo apt-get install --no-install-recommends llvm-17-dev
go install -v ./...
```
## Demo
The `_demo` directory contains our demos (it start with `_` to prevent the `go` command from compiling it):
* [hello](_demo/hello/hello.go): call C printf to print `Hello world`
* [concat](_demo/concat/concat.go): call C fprintf with stderr, and Go variadic function
* [qsort](_demo/qsort/qsort.go): call C function with a callback (eg. qsort)
* [genints](_demo/genints/genints.go): various forms of closure usage (including C function, recv.method and anonymous function)
### How to run demos
```sh
cd <demo-directory> # eg. cd _demo/genints
llgo run .
```

21
_demo/concat/concat.go Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"github.com/goplus/llgo/c"
)
func concat(args ...string) (ret string) {
for _, v := range args {
ret += v
}
return
}
func main() {
result := concat("Hello", " ", "World")
c.Fprintf(c.Stderr, c.Str("Hi, %s\n"), c.AllocaCStr(result))
}
/* Expected output (stderr):
Hi, Hello World
*/

63
_demo/genints/gen_ints.go Normal file
View File

@@ -0,0 +1,63 @@
package main
import (
"github.com/goplus/llgo/c"
)
type generator struct {
val c.Int
}
func (g *generator) next() c.Int {
g.val++
return g.val
}
func genInts(n int, gen func() c.Int) []c.Int {
a := make([]c.Int, n)
for i := range a {
a[i] = gen()
}
return a
}
func main() {
// generate 5 random integers
for _, v := range genInts(5, c.Rand) {
c.Printf(c.Str("%d\n"), v)
}
// generate 5 integers, each is double of the previous one
initVal := c.Int(1)
ints := genInts(5, func() c.Int {
initVal *= 2
return initVal
})
for _, v := range ints {
c.Printf(c.Str("%d\n"), v)
}
// generate 5 integers, each is incremented by 1
g := &generator{val: 1}
for _, v := range genInts(5, g.next) {
c.Printf(c.Str("%d\n"), v)
}
}
/* Posible output:
16807
282475249
1622650073
984943658
1144108930
2
4
8
16
32
2
3
4
5
6
*/

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

@@ -0,0 +1,13 @@
package main
import (
"github.com/goplus/llgo/c"
)
func main() {
c.Printf(c.Str("Hello world\n"))
}
/* Expected output:
Hello World
*/

25
_demo/qsort/qsort.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import (
"unsafe"
"github.com/goplus/llgo/c"
)
func main() {
a := [...]int{100, 8, 23, 2, 7}
c.Qsort(c.Pointer(&a), 5, unsafe.Sizeof(0), func(a, b c.Pointer) c.Int {
return c.Int(*(*int)(a) - *(*int)(b))
})
for _, v := range a {
c.Printf(c.Str("%d\n"), v)
}
}
/* Expected output:
2
7
8
23
100
*/

76
c/c.go Normal file
View File

@@ -0,0 +1,76 @@
/*
* 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 c
import "C"
import "unsafe"
const (
LLGoPackage = "decl"
)
type (
Char = int8
Int = C.int
Pointer = unsafe.Pointer
FilePtr = unsafe.Pointer
)
//go:linkname Stdin __stdinp
var Stdin FilePtr
//go:linkname Stdout __stdoutp
var Stdout FilePtr
//go:linkname Stderr __stderrp
var Stderr FilePtr
//go:linkname Str llgo.cstr
func Str(string) *Char
//go:linkname Advance llgo.advance
func Advance(ptr Pointer, offset int) Pointer
//go:linkname Alloca llgo.alloca
func Alloca(size uintptr) Pointer
//go:linkname AllocaCStr llgo.allocaCStr
func AllocaCStr(s string) *Char
//go:linkname Unreachable llgo.unreachable
func Unreachable()
//go:linkname Rand C.rand
func Rand() Int
//go:linkname Malloc C.malloc
func Malloc(size uintptr) Pointer
//go:linkname Memcpy C.memcpy
func Memcpy(dst, src Pointer, n uintptr) Pointer
//go:linkname Memset C.memset
func Memset(s Pointer, c Int, n uintptr) Pointer
//go:linkname Printf C.printf
func Printf(format *Char, __llgo_va_list ...any) Int
//go:linkname Fprintf C.fprintf
func Fprintf(fp FilePtr, format *Char, __llgo_va_list ...any) Int
//go:linkname Qsort C.qsort
func Qsort(base Pointer, count, elem uintptr, compar func(a, b Pointer) Int)

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"github.com/goplus/llgo/x/clang/parser"
)
var (
dump = flag.Bool("dump", false, "dump AST")
)
func usage() {
fmt.Fprintf(os.Stderr, "Usage: clangast [-dump] source.i\n")
flag.PrintDefaults()
}
func main() {
flag.Usage = usage
flag.Parse()
if flag.NArg() < 1 {
usage()
return
}
var file = flag.Arg(0)
var err error
if *dump {
doc, _, e := parser.DumpAST(file, nil)
if e == nil {
os.Stdout.Write(doc)
return
}
err = e
} else {
doc, _, e := parser.ParseFile(file, 0)
if e == nil {
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
check(enc.Encode(doc))
return
}
err = e
}
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
func check(err error) {
if err != nil {
panic(err)
}
}

41
chore/clangpp/clangpp.go Normal file
View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"fmt"
"os"
"github.com/goplus/llgo/x/clang/preprocessor"
)
func usage() {
fmt.Fprintf(os.Stderr, "Usage: clangpp source.c\n")
}
func main() {
if len(os.Args) < 2 {
usage()
return
}
infile := os.Args[1]
outfile := infile + ".i"
if err := preprocessor.Do(infile, outfile, nil); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

View File

@@ -49,6 +49,6 @@ _llgo_0:
ret void ret void
} }
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare void @printf(ptr, ...)

174
cl/_testdata/print/in.go Normal file
View File

@@ -0,0 +1,174 @@
package main
import (
"unsafe"
"github.com/goplus/llgo/internal/runtime/c"
)
func gwrite(b []byte) {
if len(b) == 0 {
return
}
for _, v := range b {
c.Printf(c.Str("%c"), v)
}
}
// func printfloat(v float64) {
// switch {
// case v != v:
// printstring("NaN")
// return
// case v+v == v && v > 0:
// printstring("+Inf")
// return
// case v+v == v && v < 0:
// printstring("-Inf")
// return
// }
// const n = 7 // digits printed
// var buf [n + 7]byte
// buf[0] = '+'
// e := 0 // exp
// if v == 0 {
// if 1/v < 0 {
// buf[0] = '-'
// }
// } else {
// if v < 0 {
// v = -v
// buf[0] = '-'
// }
// // normalize
// for v >= 10 {
// e++
// v /= 10
// }
// for v < 1 {
// e--
// v *= 10
// }
// // round
// h := 5.0
// for i := 0; i < n; i++ {
// h /= 10
// }
// v += h
// if v >= 10 {
// e++
// v /= 10
// }
// }
// // format +d.dddd+edd
// for i := 0; i < n; i++ {
// s := int(v)
// buf[i+2] = byte(s + '0')
// v -= float64(s)
// v *= 10
// }
// buf[1] = buf[2]
// buf[2] = '.'
// buf[n+2] = 'e'
// buf[n+3] = '+'
// if e < 0 {
// e = -e
// buf[n+3] = '-'
// }
// buf[n+4] = byte(e/100) + '0'
// buf[n+5] = byte(e/10)%10 + '0'
// buf[n+6] = byte(e%10) + '0'
// gwrite(buf[:])
// }
func printuint(v uint64) {
var buf [100]byte
i := len(buf)
for i--; i > 0; i-- {
buf[i] = byte(v%10 + '0')
if v < 10 {
break
}
v /= 10
}
gwrite(buf[i:])
}
// func printint(v int64) {
// if v < 0 {
// printstring("-")
// v = -v
// }
// printuint(uint64(v))
// }
var minhexdigits = 0
func printhex(v uint64) {
const dig = "0123456789abcdef"
var buf [100]byte
i := len(buf)
for i--; i > 0; i-- {
buf[i] = dig[v%16]
if v < 16 && len(buf)-i >= minhexdigits {
break
}
v /= 16
}
i--
buf[i] = 'x'
i--
buf[i] = '0'
gwrite(buf[i:])
}
func printsp() {
printstring(" ")
}
func printnl() {
printstring("\n")
}
func printstring(s string) {
gwrite(bytes(s))
}
type slice struct {
array unsafe.Pointer
len int
cap int
}
type stringStruct struct {
str unsafe.Pointer
len int
}
func stringStructOf(sp *string) *stringStruct {
return (*stringStruct)(unsafe.Pointer(sp))
}
func bytes(s string) (ret []byte) {
rp := (*slice)(unsafe.Pointer(&ret))
sp := stringStructOf(&s)
rp.array = sp.str
rp.len = sp.len
rp.cap = sp.len
return
}
func main() {
printstring("llgo")
printnl()
printuint(1024)
printnl()
printhex(0x1234abcf)
printnl()
}

213
cl/_testdata/print/out.ll Normal file
View File

@@ -0,0 +1,213 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
%main.stringStruct = type { ptr, i64 }
%main.slice = type { ptr, i64, i64 }
@"main.init$guard" = global ptr null
@main.minhexdigits = global ptr null
@0 = private unnamed_addr constant [3 x i8] c"%c\00", align 1
@1 = private unnamed_addr constant [5 x i8] c"llgo\00", align 1
@2 = private unnamed_addr constant [17 x i8] c"0123456789abcdef\00", align 1
@3 = private unnamed_addr constant [2 x i8] c"\0A\00", align 1
@4 = private unnamed_addr constant [2 x i8] c" \00", align 1
define %"github.com/goplus/llgo/internal/runtime.Slice" @main.bytes(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %1, align 8
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 24)
%3 = call ptr @main.stringStructOf(ptr %1)
%4 = getelementptr inbounds %main.stringStruct, ptr %3, i32 0, i32 0
%5 = load ptr, ptr %4, align 8
%6 = getelementptr inbounds %main.slice, ptr %2, i32 0, i32 0
store ptr %5, ptr %6, align 8
%7 = getelementptr inbounds %main.stringStruct, ptr %3, i32 0, i32 1
%8 = load i64, ptr %7, align 4
%9 = getelementptr inbounds %main.slice, ptr %2, i32 0, i32 1
store i64 %8, ptr %9, align 4
%10 = getelementptr inbounds %main.stringStruct, ptr %3, i32 0, i32 1
%11 = load i64, ptr %10, align 4
%12 = getelementptr inbounds %main.slice, ptr %2, i32 0, i32 2
store i64 %11, ptr %12, align 4
%13 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %2, align 8
ret %"github.com/goplus/llgo/internal/runtime.Slice" %13
}
define void @main.gwrite(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
%2 = icmp eq i64 %1, 0
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
ret void
_llgo_2: ; preds = %_llgo_0
%3 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
br label %_llgo_3
_llgo_3: ; preds = %_llgo_4, %_llgo_2
%4 = phi i64 [ -1, %_llgo_2 ], [ %5, %_llgo_4 ]
%5 = add i64 %4, 1
%6 = icmp slt i64 %5, %3
br i1 %6, label %_llgo_4, label %_llgo_5
_llgo_4: ; preds = %_llgo_3
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
%8 = getelementptr inbounds i8, ptr %7, i64 %5
%9 = load i8, ptr %8, align 1
%10 = call i32 (ptr, ...) @printf(ptr @0, i8 %9)
br label %_llgo_3
_llgo_5: ; preds = %_llgo_3
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 i64 0, ptr @main.minhexdigits, align 4
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 4)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %0)
call void @main.printnl()
call void @main.printuint(i64 1024)
call void @main.printnl()
call void @main.printhex(i64 305441743)
call void @main.printnl()
ret void
}
define void @main.printhex(i64 %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 100)
br label %_llgo_3
_llgo_1: ; preds = %_llgo_3
%2 = urem i64 %14, 16
%3 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 16)
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %3)
%5 = getelementptr inbounds i8, ptr %4, i64 %2
%6 = load i8, ptr %5, align 1
%7 = getelementptr inbounds i8, ptr %1, i64 %15
store i8 %6, ptr %7, align 1
%8 = icmp ult i64 %14, 16
br i1 %8, label %_llgo_5, label %_llgo_4
_llgo_2: ; preds = %_llgo_5, %_llgo_3
%9 = sub i64 %15, 1
%10 = getelementptr inbounds i8, ptr %1, i64 %9
store i8 120, ptr %10, align 1
%11 = sub i64 %9, 1
%12 = getelementptr inbounds i8, ptr %1, i64 %11
store i8 48, ptr %12, align 1
%13 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %1, i64 1, i64 100, i64 %11, i64 100, i64 100)
call void @main.gwrite(%"github.com/goplus/llgo/internal/runtime.Slice" %13)
ret void
_llgo_3: ; preds = %_llgo_4, %_llgo_0
%14 = phi i64 [ %0, %_llgo_0 ], [ %17, %_llgo_4 ]
%15 = phi i64 [ 99, %_llgo_0 ], [ %18, %_llgo_4 ]
%16 = icmp sgt i64 %15, 0
br i1 %16, label %_llgo_1, label %_llgo_2
_llgo_4: ; preds = %_llgo_5, %_llgo_1
%17 = udiv i64 %14, 16
%18 = sub i64 %15, 1
br label %_llgo_3
_llgo_5: ; preds = %_llgo_1
%19 = sub i64 100, %15
%20 = load i64, ptr @main.minhexdigits, align 4
%21 = icmp sge i64 %19, %20
br i1 %21, label %_llgo_2, label %_llgo_4
}
define void @main.printnl() {
_llgo_0:
%0 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 1)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %0)
ret void
}
define void @main.printsp() {
_llgo_0:
%0 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @4, i64 1)
call void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %0)
ret void
}
define void @main.printstring(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0:
%1 = call %"github.com/goplus/llgo/internal/runtime.Slice" @main.bytes(%"github.com/goplus/llgo/internal/runtime.String" %0)
call void @main.gwrite(%"github.com/goplus/llgo/internal/runtime.Slice" %1)
ret void
}
define void @main.printuint(i64 %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 100)
br label %_llgo_3
_llgo_1: ; preds = %_llgo_3
%2 = urem i64 %8, 10
%3 = add i64 %2, 48
%4 = trunc i64 %3 to i8
%5 = getelementptr inbounds i8, ptr %1, i64 %9
store i8 %4, ptr %5, align 1
%6 = icmp ult i64 %8, 10
br i1 %6, label %_llgo_2, label %_llgo_4
_llgo_2: ; preds = %_llgo_3, %_llgo_1
%7 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %1, i64 1, i64 100, i64 %9, i64 100, i64 100)
call void @main.gwrite(%"github.com/goplus/llgo/internal/runtime.Slice" %7)
ret void
_llgo_3: ; preds = %_llgo_4, %_llgo_0
%8 = phi i64 [ %0, %_llgo_0 ], [ %11, %_llgo_4 ]
%9 = phi i64 [ 99, %_llgo_0 ], [ %12, %_llgo_4 ]
%10 = icmp sgt i64 %9, 0
br i1 %10, label %_llgo_1, label %_llgo_2
_llgo_4: ; preds = %_llgo_1
%11 = udiv i64 %8, 10
%12 = sub i64 %9, 1
br label %_llgo_3
}
define ptr @main.stringStructOf(ptr %0) {
_llgo_0:
ret ptr %0
}
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare i32 @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
declare ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String")
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)

View File

@@ -32,6 +32,6 @@ _llgo_0:
ret void ret void
} }
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare void @printf(ptr, ...)

View File

@@ -35,6 +35,6 @@ _llgo_0:
ret void ret void
} }
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare void @printf(ptr, ...)

18
cl/_testrt/callback/in.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func callback(msg *c.Char, f func(*c.Char)) {
f(msg)
}
func print(msg *c.Char) {
c.Printf(msg)
}
func main() {
callback(c.Str("Hello\n"), print)
callback(c.Str("callback\n"), print)
}

View File

@@ -0,0 +1,64 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [7 x i8] c"Hello\0A\00", align 1
@1 = private unnamed_addr constant [10 x i8] c"callback\0A\00", align 1
define void @main.callback(ptr %0, { ptr, ptr } %1) {
_llgo_0:
%2 = extractvalue { ptr, ptr } %1, 1
%3 = extractvalue { ptr, ptr } %1, 0
call void %3(ptr %2, ptr %0)
ret void
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = alloca { ptr, ptr }, align 8
%1 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 0
store ptr @__llgo_stub.main.print, ptr %1, align 8
%2 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 1
store ptr null, ptr %2, align 8
%3 = load { ptr, ptr }, ptr %0, align 8
call void @main.callback(ptr @0, { ptr, ptr } %3)
%4 = alloca { ptr, ptr }, align 8
%5 = getelementptr inbounds { ptr, ptr }, ptr %4, i32 0, i32 0
store ptr @__llgo_stub.main.print, ptr %5, align 8
%6 = getelementptr inbounds { ptr, ptr }, ptr %4, i32 0, i32 1
store ptr null, ptr %6, align 8
%7 = load { ptr, ptr }, ptr %4, align 8
call void @main.callback(ptr @1, { ptr, ptr } %7)
ret void
}
define void @main.print(ptr %0) {
_llgo_0:
%1 = call i32 (ptr, ...) @printf(ptr %0)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
define linkonce void @__llgo_stub.main.print(ptr %0, ptr %1) {
_llgo_0:
tail call void @main.print(ptr %1)
ret void
}
declare i32 @printf(ptr, ...)

18
cl/_testrt/closure/in.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func main() {
func(n1, n2 int) {
c.Printf(c.Str("%d %d\n"), n1, n2)
}(100, 200)
fn1 := func(n1, n2 int) {
c.Printf(c.Str("%d %d\n"), n1, n2)
}
fn2 := func() {
fn1(100, 200)
}
fn2()
}

84
cl/_testrt/closure/out.ll Normal file
View File

@@ -0,0 +1,84 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1
@1 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
call void @"main.main$1"(i64 100, i64 200)
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 16)
%1 = alloca { ptr, ptr }, align 8
%2 = getelementptr inbounds { ptr, ptr }, ptr %1, i32 0, i32 0
store ptr @"__llgo_stub.main.main$2", ptr %2, align 8
%3 = getelementptr inbounds { ptr, ptr }, ptr %1, i32 0, i32 1
store ptr null, ptr %3, align 8
%4 = load { ptr, ptr }, ptr %1, align 8
store { ptr, ptr } %4, ptr %0, align 8
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8)
%6 = getelementptr inbounds { ptr }, ptr %5, i32 0, i32 0
store ptr %0, ptr %6, align 8
%7 = alloca { ptr, ptr }, align 8
%8 = getelementptr inbounds { ptr, ptr }, ptr %7, i32 0, i32 0
store ptr @"main.main$3", ptr %8, align 8
%9 = getelementptr inbounds { ptr, ptr }, ptr %7, i32 0, i32 1
store ptr %5, ptr %9, align 8
%10 = load { ptr, ptr }, ptr %7, align 8
%11 = extractvalue { ptr, ptr } %10, 1
%12 = extractvalue { ptr, ptr } %10, 0
call void %12(ptr %11)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
define void @"main.main$1"(i64 %0, i64 %1) {
_llgo_0:
%2 = call i32 (ptr, ...) @printf(ptr @0, i64 %0, i64 %1)
ret void
}
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
define void @"main.main$2"(i64 %0, i64 %1) {
_llgo_0:
%2 = call i32 (ptr, ...) @printf(ptr @1, i64 %0, i64 %1)
ret void
}
define linkonce void @"__llgo_stub.main.main$2"(ptr %0, i64 %1, i64 %2) {
_llgo_0:
tail call void @"main.main$2"(i64 %1, i64 %2)
ret void
}
define void @"main.main$3"(ptr %0) {
_llgo_0:
%1 = load { ptr }, ptr %0, align 8
%2 = extractvalue { ptr } %1, 0
%3 = load { ptr, ptr }, ptr %2, align 8
%4 = extractvalue { ptr, ptr } %3, 1
%5 = extractvalue { ptr, ptr } %3, 0
call void %5(ptr %4, i64 100, i64 200)
ret void
}
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64)
declare i32 @printf(ptr, ...)

View File

@@ -25,6 +25,6 @@ _llgo_0:
ret void ret void
} }
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare void @printf(ptr, ...)

View File

@@ -5,8 +5,6 @@ source_filename = "main"
@__stderrp = external global ptr @__stderrp = external global ptr
@0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1 @0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1
declare void @fprintf(ptr, ptr, ...)
define void @main.init() { define void @main.init() {
_llgo_0: _llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1 %0 = load i1, ptr @"main.init$guard", align 1
@@ -30,3 +28,5 @@ _llgo_0:
} }
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare void @fprintf(ptr, ptr, ...)

17
cl/_testrt/gotypes/in.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type base interface {
f(m map[string]func())
}
type bar interface {
base
g(c chan func())
}
func foo(bar) {
}
func main() {
foo(nil)
}

34
cl/_testrt/gotypes/out.ll Normal file
View File

@@ -0,0 +1,34 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
@"main.init$guard" = global ptr null
define void @main.foo(%"github.com/goplus/llgo/internal/runtime.iface" %0) {
_llgo_0:
ret void
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
call void @main.foo(%"github.com/goplus/llgo/internal/runtime.iface" zeroinitializer)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -4,6 +4,15 @@ import (
"github.com/goplus/llgo/internal/runtime/c" "github.com/goplus/llgo/internal/runtime/c"
) )
type generator struct {
val c.Int
}
func (g *generator) next() c.Int {
g.val++
return g.val
}
func genInts(n int, gen func() c.Int) []c.Int { func genInts(n int, gen func() c.Int) []c.Int {
a := make([]c.Int, n) a := make([]c.Int, n)
for i := range a { for i := range a {
@@ -13,8 +22,21 @@ func genInts(n int, gen func() c.Int) []c.Int {
} }
func main() { func main() {
a := genInts(5, c.Rand) for _, v := range genInts(5, c.Rand) {
for _, v := range a { c.Printf(c.Str("%d\n"), v)
}
initVal := c.Int(1)
ints := genInts(5, func() c.Int {
initVal *= 2
return initVal
})
for _, v := range ints {
c.Printf(c.Str("%d\n"), v)
}
g := &generator{val: 1}
for _, v := range genInts(5, g.next) {
c.Printf(c.Str("%d\n"), v) c.Printf(c.Str("%d\n"), v)
} }
} }

View File

@@ -2,9 +2,12 @@
source_filename = "main" source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 } %"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
%main.generator = type { i32 }
@"main.init$guard" = global ptr null @"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 @0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@1 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@2 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
define %"github.com/goplus/llgo/internal/runtime.Slice" @main.genInts(i64 %0, { ptr, ptr } %1) { define %"github.com/goplus/llgo/internal/runtime.Slice" @main.genInts(i64 %0, { ptr, ptr } %1) {
_llgo_0: _llgo_0:
@@ -21,16 +24,30 @@ _llgo_1: ; preds = %_llgo_2, %_llgo_0
br i1 %8, label %_llgo_2, label %_llgo_3 br i1 %8, label %_llgo_2, label %_llgo_3
_llgo_2: ; preds = %_llgo_1 _llgo_2: ; preds = %_llgo_1
%9 = extractvalue { ptr, ptr } %1, 0 %9 = extractvalue { ptr, ptr } %1, 1
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %4) %10 = extractvalue { ptr, ptr } %1, 0
%11 = getelementptr inbounds i32, ptr %10, i64 %7 %11 = call i32 %10(ptr %9)
store i32 0, ptr %11, align 4 %12 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %4)
%13 = getelementptr inbounds i32, ptr %12, i64 %7
store i32 %11, ptr %13, align 4
br label %_llgo_1 br label %_llgo_1
_llgo_3: ; preds = %_llgo_1 _llgo_3: ; preds = %_llgo_1
ret %"github.com/goplus/llgo/internal/runtime.Slice" %4 ret %"github.com/goplus/llgo/internal/runtime.Slice" %4
} }
define i32 @"(*main.generator).next"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %main.generator, ptr %0, i32 0, i32 0
%2 = load i32, ptr %1, align 4
%3 = add i32 %2, 1
%4 = getelementptr inbounds %main.generator, ptr %0, i32 0, i32 0
store i32 %3, ptr %4, align 4
%5 = getelementptr inbounds %main.generator, ptr %0, i32 0, i32 0
%6 = load i32, ptr %5, align 4
ret i32 %6
}
define void @main.init() { define void @main.init() {
_llgo_0: _llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1 %0 = load i1, ptr @"main.init$guard", align 1
@@ -50,7 +67,7 @@ _llgo_0:
call void @main.init() call void @main.init()
%0 = alloca { ptr, ptr }, align 8 %0 = alloca { ptr, ptr }, align 8
%1 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 0 %1 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 0
store ptr @rand, ptr %1, align 8 store ptr @__llgo_stub.rand, ptr %1, align 8
%2 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 1 %2 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 1
store ptr null, ptr %2, align 8 store ptr null, ptr %2, align 8
%3 = load { ptr, ptr }, ptr %0, align 8 %3 = load { ptr, ptr }, ptr %0, align 8
@@ -72,6 +89,65 @@ _llgo_2: ; preds = %_llgo_1
br label %_llgo_1 br label %_llgo_1
_llgo_3: ; preds = %_llgo_1 _llgo_3: ; preds = %_llgo_1
%13 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 4)
store i32 1, ptr %13, align 4
%14 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8)
%15 = getelementptr inbounds { ptr }, ptr %14, i32 0, i32 0
store ptr %13, ptr %15, align 8
%16 = alloca { ptr, ptr }, align 8
%17 = getelementptr inbounds { ptr, ptr }, ptr %16, i32 0, i32 0
store ptr @"main.main$1", ptr %17, align 8
%18 = getelementptr inbounds { ptr, ptr }, ptr %16, i32 0, i32 1
store ptr %14, ptr %18, align 8
%19 = load { ptr, ptr }, ptr %16, align 8
%20 = call %"github.com/goplus/llgo/internal/runtime.Slice" @main.genInts(i64 5, { ptr, ptr } %19)
%21 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %20)
br label %_llgo_4
_llgo_4: ; preds = %_llgo_5, %_llgo_3
%22 = phi i64 [ -1, %_llgo_3 ], [ %23, %_llgo_5 ]
%23 = add i64 %22, 1
%24 = icmp slt i64 %23, %21
br i1 %24, label %_llgo_5, label %_llgo_6
_llgo_5: ; preds = %_llgo_4
%25 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %20)
%26 = getelementptr inbounds i32, ptr %25, i64 %23
%27 = load i32, ptr %26, align 4
%28 = call i32 (ptr, ...) @printf(ptr @1, i32 %27)
br label %_llgo_4
_llgo_6: ; preds = %_llgo_4
%29 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 4)
%30 = getelementptr inbounds %main.generator, ptr %29, i32 0, i32 0
store i32 1, ptr %30, align 4
%31 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8)
%32 = getelementptr inbounds { ptr }, ptr %31, i32 0, i32 0
store ptr %29, ptr %32, align 8
%33 = alloca { ptr, ptr }, align 8
%34 = getelementptr inbounds { ptr, ptr }, ptr %33, i32 0, i32 0
store ptr @"main.next$bound", ptr %34, align 8
%35 = getelementptr inbounds { ptr, ptr }, ptr %33, i32 0, i32 1
store ptr %31, ptr %35, align 8
%36 = load { ptr, ptr }, ptr %33, align 8
%37 = call %"github.com/goplus/llgo/internal/runtime.Slice" @main.genInts(i64 5, { ptr, ptr } %36)
%38 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %37)
br label %_llgo_7
_llgo_7: ; preds = %_llgo_8, %_llgo_6
%39 = phi i64 [ -1, %_llgo_6 ], [ %40, %_llgo_8 ]
%40 = add i64 %39, 1
%41 = icmp slt i64 %40, %38
br i1 %41, label %_llgo_8, label %_llgo_9
_llgo_8: ; preds = %_llgo_7
%42 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %37)
%43 = getelementptr inbounds i32, ptr %42, i64 %40
%44 = load i32, ptr %43, align 4
%45 = call i32 (ptr, ...) @printf(ptr @2, i32 %44)
br label %_llgo_7
_llgo_9: ; preds = %_llgo_7
ret void ret void
} }
@@ -87,4 +163,33 @@ declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i32 @rand() declare i32 @rand()
define linkonce i32 @__llgo_stub.rand(ptr %0) {
_llgo_0:
%1 = tail call i32 @rand()
ret i32 %1
}
declare i32 @printf(ptr, ...) declare i32 @printf(ptr, ...)
define i32 @"main.main$1"(ptr %0) {
_llgo_0:
%1 = load { ptr }, ptr %0, align 8
%2 = extractvalue { ptr } %1, 0
%3 = load i32, ptr %2, align 4
%4 = mul i32 %3, 2
%5 = extractvalue { ptr } %1, 0
store i32 %4, ptr %5, align 4
%6 = extractvalue { ptr } %1, 0
%7 = load i32, ptr %6, align 4
ret i32 %7
}
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64)
define i32 @"main.next$bound"(ptr %0) {
_llgo_0:
%1 = load { ptr }, ptr %0, align 8
%2 = extractvalue { ptr } %1, 0
%3 = call i32 @"(*main.generator).next"(ptr %2)
ret i32 %3
}

View File

@@ -1,13 +1,10 @@
package main package main
/*
import ( import (
"github.com/goplus/llgo/internal/runtime/c" "github.com/goplus/llgo/internal/runtime/c"
) )
*/
func main() { func main() {
a := map[int]int{23: 100, 7: 29} a := map[int]int{23: 100, 7: 29}
_ = a c.Printf(c.Str("Hello %d\n"), a[23])
// c.Printf(c.Str("Hello %d\n"), a[23])
} }

View File

@@ -2,6 +2,7 @@
source_filename = "main" source_filename = "main"
@"main.init$guard" = global ptr null @"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1
define void @main.init() { define void @main.init() {
_llgo_0: _llgo_0:
@@ -21,9 +22,12 @@ _llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"() call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init() call void @main.init()
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"() %0 = call ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"()
%1 = call i32 (ptr, ...) @printf(ptr @0, <null operand!>)
ret void ret void
} }
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"() declare ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"()
declare i32 @printf(ptr, ...)

View File

@@ -53,12 +53,12 @@ _llgo_3: ; preds = %_llgo_1
ret void ret void
} }
declare void @qsort(ptr, i64, i64, ptr)
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
declare void @qsort(ptr, i64, i64, ptr)
define i32 @"main.main$1"(ptr %0, ptr %1) { define i32 @"main.main$1"(ptr %0, ptr %1) {
_llgo_0: _llgo_0:
%2 = load i64, ptr %0, align 4 %2 = load i64, ptr %0, align 4

29
cl/_testrt/result/in.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func main() {
fn := func() func(int, int) int {
return func(x, y int) int {
return x + y
}
}()
c.Printf(c.Str("%d\n"), fn(100, 200))
c.Printf(c.Str("%d\n"), add()(100, 200))
fn, n := add2()
c.Printf(c.Str("%d %d\n"), add()(100, 200), n)
}
func add() func(int, int) int {
return func(x, y int) int {
return x + y
}
}
func add2() (func(int, int) int, int) {
return func(x, y int) int {
return x + y
}, 1
}

120
cl/_testrt/result/out.ll Normal file
View File

@@ -0,0 +1,120 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@1 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@2 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1
define { ptr, ptr } @main.add() {
_llgo_0:
%0 = alloca { ptr, ptr }, align 8
%1 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 0
store ptr @"__llgo_stub.main.add$1", ptr %1, align 8
%2 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 1
store ptr null, ptr %2, align 8
%3 = load { ptr, ptr }, ptr %0, align 8
ret { ptr, ptr } %3
}
define { { ptr, ptr }, i64 } @main.add2() {
_llgo_0:
%0 = alloca { ptr, ptr }, align 8
%1 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 0
store ptr @"__llgo_stub.main.add2$1", ptr %1, align 8
%2 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 1
store ptr null, ptr %2, align 8
%3 = load { ptr, ptr }, ptr %0, align 8
%mrv = insertvalue { { ptr, ptr }, i64 } poison, { ptr, ptr } %3, 0
%mrv1 = insertvalue { { ptr, ptr }, i64 } %mrv, i64 1, 1
ret { { ptr, ptr }, i64 } %mrv1
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call { ptr, ptr } @"main.main$1"()
%1 = extractvalue { ptr, ptr } %0, 1
%2 = extractvalue { ptr, ptr } %0, 0
%3 = call i64 %2(ptr %1, i64 100, i64 200)
%4 = call i32 (ptr, ...) @printf(ptr @0, i64 %3)
%5 = call { ptr, ptr } @main.add()
%6 = extractvalue { ptr, ptr } %5, 1
%7 = extractvalue { ptr, ptr } %5, 0
%8 = call i64 %7(ptr %6, i64 100, i64 200)
%9 = call i32 (ptr, ...) @printf(ptr @1, i64 %8)
%10 = call { { ptr, ptr }, i64 } @main.add2()
%11 = extractvalue { { ptr, ptr }, i64 } %10, 0
%12 = extractvalue { { ptr, ptr }, i64 } %10, 1
%13 = call { ptr, ptr } @main.add()
%14 = extractvalue { ptr, ptr } %13, 1
%15 = extractvalue { ptr, ptr } %13, 0
%16 = call i64 %15(ptr %14, i64 100, i64 200)
%17 = call i32 (ptr, ...) @printf(ptr @2, i64 %16, i64 %12)
ret void
}
define i64 @"main.add$1"(i64 %0, i64 %1) {
_llgo_0:
%2 = add i64 %0, %1
ret i64 %2
}
define linkonce i64 @"__llgo_stub.main.add$1"(ptr %0, i64 %1, i64 %2) {
_llgo_0:
%3 = tail call i64 @"main.add$1"(i64 %1, i64 %2)
ret i64 %3
}
define i64 @"main.add2$1"(i64 %0, i64 %1) {
_llgo_0:
%2 = add i64 %0, %1
ret i64 %2
}
define linkonce i64 @"__llgo_stub.main.add2$1"(ptr %0, i64 %1, i64 %2) {
_llgo_0:
%3 = tail call i64 @"main.add2$1"(i64 %1, i64 %2)
ret i64 %3
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
define { ptr, ptr } @"main.main$1"() {
_llgo_0:
%0 = alloca { ptr, ptr }, align 8
%1 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 0
store ptr @"__llgo_stub.main.main$1$1", ptr %1, align 8
%2 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 1
store ptr null, ptr %2, align 8
%3 = load { ptr, ptr }, ptr %0, align 8
ret { ptr, ptr } %3
}
declare i32 @printf(ptr, ...)
define i64 @"main.main$1$1"(i64 %0, i64 %1) {
_llgo_0:
%2 = add i64 %0, %1
ret i64 %2
}
define linkonce i64 @"__llgo_stub.main.main$1$1"(ptr %0, i64 %1, i64 %2) {
_llgo_0:
%3 = tail call i64 @"main.main$1$1"(i64 %1, i64 %2)
ret i64 %3
}

View File

@@ -36,8 +36,8 @@ _llgo_0:
ret void ret void
} }
declare void @printf(ptr, ...) declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i32 @strlen(ptr) declare i32 @strlen(ptr)
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @printf(ptr, ...)

View File

@@ -70,8 +70,8 @@ _llgo_0:
ret void ret void
} }
declare void @printf(ptr, ...)
declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64) declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64)
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"() declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -25,6 +25,7 @@ import (
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
) )
/*
func TestErrCompileValue(t *testing.T) { func TestErrCompileValue(t *testing.T) {
defer func() { defer func() {
if r := recover(); r != "can't use llgo instruction as a value" { if r := recover(); r != "can't use llgo instruction as a value" {
@@ -43,6 +44,7 @@ func TestErrCompileValue(t *testing.T) {
Signature: types.NewSignatureType(nil, nil, nil, nil, nil, false), Signature: types.NewSignatureType(nil, nil, nil, nil, nil, false),
}) })
} }
*/
func TestErrCompileInstrOrValue(t *testing.T) { func TestErrCompileInstrOrValue(t *testing.T) {
defer func() { defer func() {

View File

@@ -38,7 +38,7 @@ import (
func init() { func init() {
cl.SetDebug(cl.DbgFlagAll) cl.SetDebug(cl.DbgFlagAll)
llssa.Initialize(llssa.InitAll) llssa.Initialize(llssa.InitAll | llssa.InitNative)
llssa.SetDebug(llssa.DbgFlagAll) llssa.SetDebug(llssa.DbgFlagAll)
} }

View File

@@ -169,7 +169,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
for i, n := 0, mthds.Len(); i < n; i++ { for i, n := 0, mthds.Len(); i < n; i++ {
mthd := mthds.At(i) mthd := mthds.At(i)
if ssaMthd := prog.MethodValue(mthd); ssaMthd != nil { if ssaMthd := prog.MethodValue(mthd); ssaMthd != nil {
p.compileFunc(pkg, mthd.Obj().Pkg(), ssaMthd, false) p.compileFuncDecl(pkg, mthd.Obj().Pkg(), ssaMthd)
} }
} }
} }
@@ -184,68 +184,108 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
if debugInstr { if debugInstr {
log.Println("==> NewVar", name, typ) log.Println("==> NewVar", name, typ)
} }
if vtype == cVar { g := pkg.NewVar(name, typ, llssa.Background(vtype))
typ = llssa.CType(typ)
}
g := pkg.NewVar(name, typ)
if vtype == goVar { if vtype == goVar {
g.Init(p.prog.Null(g.Type)) g.Init(p.prog.Null(g.Type))
} }
} }
func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function, closure bool) llssa.Function { func makeClosureCtx(pkg *types.Package, vars []*ssa.FreeVar) *types.Var {
n := len(vars)
flds := make([]*types.Var, n)
for i, v := range vars {
flds[i] = types.NewField(token.NoPos, pkg, v.Name(), v.Type(), false)
}
t := types.NewPointer(types.NewStruct(flds, nil))
return types.NewParam(token.NoPos, pkg, "__llgo_ctx", t)
}
func (p *context) compileFuncDecl(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) llssa.Function {
name, ftype := p.funcName(pkgTypes, f, true)
if ftype != goFunc {
return nil
}
fn := pkg.FuncOf(name)
if fn != nil && fn.HasBody() {
return fn
}
var sig = f.Signature var sig = f.Signature
var name string var hasCtx = len(f.FreeVars) > 0
if closure { if hasCtx {
name = funcName(pkgTypes, f)
if debugInstr { if debugInstr {
log.Println("==> NewClosure", name, "type:", sig) log.Println("==> NewClosure", name, "type:", sig)
} }
ctx := makeClosureCtx(pkgTypes, f.FreeVars)
sig = llssa.FuncAddCtx(ctx, sig)
} else { } else {
var ftype int
name, ftype = p.funcName(pkgTypes, f, true)
switch ftype {
case ignoredFunc, llgoInstr: // llgo extended instructions
return nil
}
if debugInstr { if debugInstr {
log.Println("==> NewFunc", name, "type:", sig.Recv(), sig) log.Println("==> NewFunc", name, "type:", sig.Recv(), sig)
} }
if ftype == cFunc {
sig = llssa.CFuncDecl(sig)
} }
if fn == nil {
fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx)
} }
fn := pkg.NewFunc(name, sig) if nblk := len(f.Blocks); nblk > 0 {
fn.MakeBlocks(nblk) // to set fn.HasBody() = true
p.inits = append(p.inits, func() { p.inits = append(p.inits, func() {
p.fn = fn p.fn = fn
defer func() { defer func() {
p.fn = nil p.fn = nil
}() }()
p.phis = nil p.phis = nil
nblk := len(f.Blocks)
if nblk == 0 { // external function
return
}
if debugGoSSA { if debugGoSSA {
f.WriteTo(os.Stderr) f.WriteTo(os.Stderr)
} }
if debugInstr { if debugInstr {
log.Println("==> FuncBody", name) log.Println("==> FuncBody", name)
} }
fn.MakeBlocks(nblk)
b := fn.NewBuilder() b := fn.NewBuilder()
p.bvals = make(map[ssa.Value]llssa.Expr) p.bvals = make(map[ssa.Value]llssa.Expr)
off := make([]int, len(f.Blocks))
for i, block := range f.Blocks { for i, block := range f.Blocks {
p.compileBlock(b, block, i == 0 && name == "main") off[i] = p.compilePhis(b, block)
}
for i, block := range f.Blocks {
p.compileBlock(b, block, off[i], i == 0 && name == "main")
} }
for _, phi := range p.phis { for _, phi := range p.phis {
phi() phi()
} }
}) })
}
return fn return fn
} }
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bool) llssa.BasicBlock { // funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
// or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc.
func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
pkgTypes := p.ensureLoaded(fn.Pkg.Pkg)
pkg := p.pkg
name, ftype := p.funcName(pkgTypes, fn, false)
if ftype == llgoInstr {
switch name {
case "cstr":
ftype = llgoCstr
case "advance":
ftype = llgoAdvance
case "alloca":
ftype = llgoAlloca
case "allocaCStr":
ftype = llgoAllocaCStr
case "unreachable":
ftype = llgoUnreachable
default:
panic("unknown llgo instruction: " + name)
}
} else if ret = pkg.FuncOf(name); ret == nil && len(fn.FreeVars) == 0 {
sig := fn.Signature
ret = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false)
}
return
}
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, doInit bool) llssa.BasicBlock {
ret := p.fn.Block(block.Index) ret := p.fn.Block(block.Index)
b.SetBlock(ret) b.SetBlock(ret)
if doInit { if doInit {
@@ -253,8 +293,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bo
callRuntimeInit(b, pkg) callRuntimeInit(b, pkg)
b.Call(pkg.FuncOf("main.init").Expr) b.Call(pkg.FuncOf("main.init").Expr)
} }
instrs := p.compilePhis(b, block.Instrs) for _, instr := range block.Instrs[n:] {
for _, instr := range instrs {
p.compileInstr(b, instr) p.compileInstr(b, instr)
} }
return ret return ret
@@ -265,7 +304,8 @@ const (
) )
func callRuntimeInit(b llssa.Builder, pkg llssa.Package) { func callRuntimeInit(b llssa.Builder, pkg llssa.Package) {
fn := pkg.NewFunc(RuntimeInit, types.NewSignatureType(nil, nil, nil, nil, nil, false)) sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
fn := pkg.NewFunc(RuntimeInit, sig, llssa.InC) // don't need to convert runtime.init
b.Call(fn.Expr) b.Call(fn.Expr)
} }
@@ -349,30 +389,32 @@ func isPhi(i ssa.Instruction) bool {
return ok return ok
} }
func (p *context) compilePhis(b llssa.Builder, instrs []ssa.Instruction) []ssa.Instruction { func (p *context) compilePhis(b llssa.Builder, block *ssa.BasicBlock) int {
if ninstr := len(instrs); ninstr > 0 { ret := p.fn.Block(block.Index)
if isPhi(instrs[0]) { b.SetBlock(ret)
if ninstr := len(block.Instrs); ninstr > 0 {
if isPhi(block.Instrs[0]) {
n := 1 n := 1
for n < ninstr && isPhi(instrs[n]) { for n < ninstr && isPhi(block.Instrs[n]) {
n++ n++
} }
rets := make([]llssa.Expr, n) rets := make([]llssa.Expr, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
iv := instrs[i].(*ssa.Phi) iv := block.Instrs[i].(*ssa.Phi)
rets[i] = p.compilePhi(b, iv) rets[i] = p.compilePhi(b, iv)
} }
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
iv := instrs[i].(*ssa.Phi) iv := block.Instrs[i].(*ssa.Phi)
p.bvals[iv] = rets[i].Do(b) p.bvals[iv] = rets[i].Do(b)
} }
return instrs[n:] return n
} }
} }
return instrs return 0
} }
func (p *context) compilePhi(b llssa.Builder, v *ssa.Phi) (ret llssa.Expr) { func (p *context) compilePhi(b llssa.Builder, v *ssa.Phi) (ret llssa.Expr) {
phi := b.Phi(p.prog.Type(v.Type())) phi := b.Phi(p.prog.Type(v.Type(), llssa.InGo))
ret = phi.Expr ret = phi.Expr
p.phis = append(p.phis, func() { p.phis = append(p.phis, func() {
preds := v.Block().Preds preds := v.Block().Preds
@@ -418,7 +460,7 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
ret = b.BuiltinCall(fn, args...) ret = b.BuiltinCall(fn, args...)
} }
case *ssa.Function: case *ssa.Function:
fn, ftype := p.funcOf(cv) fn, ftype := p.compileFunction(cv)
switch ftype { switch ftype {
case goFunc, cFunc: case goFunc, cFunc:
args := p.compileValues(b, call.Args, kind) args := p.compileValues(b, call.Args, kind)
@@ -451,11 +493,11 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
case *ssa.ChangeType: case *ssa.ChangeType:
t := v.Type() t := v.Type()
x := p.compileValue(b, v.X) x := p.compileValue(b, v.X)
ret = b.ChangeType(p.prog.Type(t), x) ret = b.ChangeType(p.prog.Type(t, llssa.InGo), x)
case *ssa.Convert: case *ssa.Convert:
t := v.Type() t := v.Type()
x := p.compileValue(b, v.X) x := p.compileValue(b, v.X)
ret = b.Convert(p.prog.Type(t), x) ret = b.Convert(p.prog.Type(t, llssa.InGo), x)
case *ssa.FieldAddr: case *ssa.FieldAddr:
x := p.compileValue(b, v.X) x := p.compileValue(b, v.X)
ret = b.FieldAddr(x, v.Field) ret = b.FieldAddr(x, v.Field)
@@ -464,7 +506,8 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
if p.checkVArgs(v, t) { // varargs: this is a varargs allocation if p.checkVArgs(v, t) { // varargs: this is a varargs allocation
return return
} }
ret = b.Alloc(t, v.Heap) elem := p.prog.Type(t.Elem(), llssa.InGo)
ret = b.Alloc(elem, v.Heap)
case *ssa.IndexAddr: case *ssa.IndexAddr:
vx := v.X vx := v.X
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index
@@ -482,8 +525,12 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
return p.compileValue(b, n.X) return p.compileValue(b, n.X)
} }
} }
panic(fmt.Errorf("todo addr of %v", e)) panic(fmt.Errorf("todo: addr of %v", e))
}) })
case *ssa.Lookup:
x := p.compileValue(b, v.X)
idx := p.compileValue(b, v.Index)
ret = b.Lookup(x, idx, v.CommaOk)
case *ssa.Slice: case *ssa.Slice:
vx := v.X vx := v.X
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs slice if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs slice
@@ -505,27 +552,35 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
const ( const (
delayExpr = true // varargs: don't need to convert an expr to any delayExpr = true // varargs: don't need to convert an expr to any
) )
t := v.Type() t := p.prog.Type(v.Type(), llssa.InGo)
x := p.compileValue(b, v.X) x := p.compileValue(b, v.X)
ret = b.MakeInterface(t, x, delayExpr) ret = b.MakeInterface(t, x, delayExpr)
case *ssa.MakeSlice: case *ssa.MakeSlice:
var nCap llssa.Expr var nCap llssa.Expr
t := v.Type() t := p.prog.Type(v.Type(), llssa.InGo)
nLen := p.compileValue(b, v.Len) nLen := p.compileValue(b, v.Len)
if v.Cap != nil { if v.Cap != nil {
nCap = p.compileValue(b, v.Cap) nCap = p.compileValue(b, v.Cap)
} }
ret = b.MakeSlice(p.prog.Type(t), nLen, nCap) ret = b.MakeSlice(t, nLen, nCap)
case *ssa.MakeMap: case *ssa.MakeMap:
var nReserve llssa.Expr var nReserve llssa.Expr
t := v.Type() t := p.prog.Type(v.Type(), llssa.InGo)
if v.Reserve != nil { if v.Reserve != nil {
nReserve = p.compileValue(b, v.Reserve) nReserve = p.compileValue(b, v.Reserve)
} }
ret = b.MakeMap(p.prog.Type(t), nReserve) ret = b.MakeMap(t, nReserve)
case *ssa.MakeClosure:
fn := p.compileValue(b, v.Fn)
bindings := p.compileValues(b, v.Bindings, 0)
ret = b.MakeClosure(fn, bindings)
case *ssa.TypeAssert: case *ssa.TypeAssert:
x := p.compileValue(b, v.X) x := p.compileValue(b, v.X)
ret = b.TypeAssert(x, p.prog.Type(v.AssertedType), v.CommaOk) t := p.prog.Type(v.AssertedType, llssa.InGo)
ret = b.TypeAssert(x, t, v.CommaOk)
case *ssa.Extract:
x := p.compileValue(b, v.Tuple)
ret = b.Extract(x, v.Index)
default: default:
panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv)) panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv))
} }
@@ -589,6 +644,17 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
} }
} }
func (p *context) compileFunction(v *ssa.Function) (llssa.Function, int) {
// v.Pkg == nil: means auto generated function?
if v.Pkg == p.goPkg || v.Pkg == nil {
// function in this package
if fn := p.compileFuncDecl(p.pkg, p.goTyps, v); fn != nil {
return fn, goFunc
}
}
return p.funcOf(v)
}
func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
if iv, ok := v.(instrOrValue); ok { if iv, ok := v.(instrOrValue); ok {
return p.compileInstrOrValue(b, iv, true) return p.compileInstrOrValue(b, iv, true)
@@ -602,21 +668,21 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
} }
} }
case *ssa.Function: case *ssa.Function:
if v.Blocks != nil { fn, _ := p.compileFunction(v)
fn := p.compileFunc(p.pkg, p.goTyps, v, true)
return fn.Expr
}
fn, ftype := p.funcOf(v)
if ftype >= llgoInstrBase {
panic("can't use llgo instruction as a value")
}
return fn.Expr return fn.Expr
case *ssa.Global: case *ssa.Global:
g := p.varOf(v) g := p.varOf(v)
return g.Expr return g.Expr
case *ssa.Const: case *ssa.Const:
t := types.Default(v.Type()) t := types.Default(v.Type())
return b.Const(v.Value, p.prog.Type(t)) return b.Const(v.Value, p.prog.Type(t, llssa.InGo))
case *ssa.FreeVar:
fn := v.Parent()
for idx, freeVar := range fn.FreeVars {
if freeVar == v {
return p.fn.FreeVar(b, idx)
}
}
} }
panic(fmt.Sprintf("compileValue: unknown value - %T\n", v)) panic(fmt.Sprintf("compileValue: unknown value - %T\n", v))
} }
@@ -695,7 +761,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
// Do not try to build generic (non-instantiated) functions. // Do not try to build generic (non-instantiated) functions.
continue continue
} }
ctx.compileFunc(ret, member.Pkg.Pkg, member, false) ctx.compileFuncDecl(ret, member.Pkg.Pkg, member)
case *ssa.Type: case *ssa.Type:
ctx.compileType(ret, member) ctx.compileType(ret, member)
case *ssa.Global: case *ssa.Global:

View File

@@ -191,8 +191,8 @@ func checkCgo(fnName string) bool {
const ( const (
ignoredFunc = iota ignoredFunc = iota
goFunc goFunc = int(llssa.InGo)
cFunc cFunc = int(llssa.InC)
llgoInstr = -1 llgoInstr = -1
llgoInstrBase = 0x80 llgoInstrBase = 0x80
@@ -222,8 +222,8 @@ func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (s
const ( const (
ignoredVar = iota ignoredVar = iota
goVar goVar = int(llssa.InGo)
cVar cVar = int(llssa.InC)
) )
func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, vtype int) { func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, vtype int) {
@@ -234,39 +234,12 @@ func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, vtyp
return name, goVar return name, goVar
} }
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
// or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc.
func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
pkgTypes := p.ensureLoaded(fn.Pkg.Pkg)
pkg := p.pkg
name, ftype := p.funcName(pkgTypes, fn, false)
if ftype == llgoInstr {
switch name {
case "cstr":
ftype = llgoCstr
case "advance":
ftype = llgoAdvance
case "alloca":
ftype = llgoAlloca
case "allocaCStr":
ftype = llgoAllocaCStr
case "unreachable":
ftype = llgoUnreachable
default:
panic("unknown llgo instruction: " + name)
}
} else if ret = pkg.FuncOf(name); ret == nil {
ret = pkg.NewFunc(name, fn.Signature)
}
return
}
func (p *context) varOf(v *ssa.Global) (ret llssa.Global) { func (p *context) varOf(v *ssa.Global) (ret llssa.Global) {
pkgTypes := p.ensureLoaded(v.Pkg.Pkg) pkgTypes := p.ensureLoaded(v.Pkg.Pkg)
pkg := p.pkg pkg := p.pkg
name, _ := p.varName(pkgTypes, v) name, vtype := p.varName(pkgTypes, v)
if ret = pkg.VarOf(name); ret == nil { if ret = pkg.VarOf(name); ret == nil {
ret = pkg.NewVar(name, v.Type()) ret = pkg.NewVar(name, v.Type(), llssa.Background(vtype))
} }
return return
} }

3
go.mod
View File

@@ -7,11 +7,14 @@ require (
github.com/goplus/gogen v1.15.2 github.com/goplus/gogen v1.15.2
github.com/goplus/llvm v0.7.5 github.com/goplus/llvm v0.7.5
github.com/goplus/mod v0.13.10 github.com/goplus/mod v0.13.10
github.com/json-iterator/go v1.1.12
github.com/qiniu/x v1.13.10 github.com/qiniu/x v1.13.10
golang.org/x/tools v0.20.0 golang.org/x/tools v0.20.0
) )
require ( require (
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.7.0 // indirect
) )

12
go.sum
View File

@@ -1,13 +1,25 @@
github.com/aykevl/go-wasm v0.0.1 h1:lPxy8l48P39W7I0tLrtCrLfZBOUq9IWZ7odGdyJP2AM= github.com/aykevl/go-wasm v0.0.1 h1:lPxy8l48P39W7I0tLrtCrLfZBOUq9IWZ7odGdyJP2AM=
github.com/aykevl/go-wasm v0.0.1/go.mod h1:b4nggwg3lEkNKOU4wzhtLKz2q2sLxSHFnc98aGt6z/Y= github.com/aykevl/go-wasm v0.0.1/go.mod h1:b4nggwg3lEkNKOU4wzhtLKz2q2sLxSHFnc98aGt6z/Y=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/goplus/gogen v1.15.2 h1:Q6XaSx/Zi5tWnjfAziYsQI6Jv6MgODRpFtOYqNkiiqM= github.com/goplus/gogen v1.15.2 h1:Q6XaSx/Zi5tWnjfAziYsQI6Jv6MgODRpFtOYqNkiiqM=
github.com/goplus/gogen v1.15.2/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk= github.com/goplus/gogen v1.15.2/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk=
github.com/goplus/llvm v0.7.5 h1:ges8WcUdu4FBi0mkZUs27p/4qDQlj28N1UpMg3VQUoE= github.com/goplus/llvm v0.7.5 h1:ges8WcUdu4FBi0mkZUs27p/4qDQlj28N1UpMg3VQUoE=
github.com/goplus/llvm v0.7.5/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= github.com/goplus/llvm v0.7.5/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
github.com/goplus/mod v0.13.10 h1:5Om6KOvo31daN7N30kWU1vC5zhsJPM+uPbcEN/FnlzE= 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/goplus/mod v0.13.10/go.mod h1:HDuPZgpWiaTp3PUolFgsiX+Q77cbUWB/mikVHfYND3c=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE= github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE=
github.com/qiniu/x v1.13.10/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E= github.com/qiniu/x v1.13.10/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=

View File

@@ -71,6 +71,11 @@ func Gen(pkgPath, inFile string, src any) string {
} }
prog := llssa.NewProgram(nil) prog := llssa.NewProgram(nil)
prog.SetRuntime(func() *types.Package {
rt, err := imp.Import(llssa.PkgRuntime)
check(err)
return rt
})
ret, err := cl.NewPackage(prog, ssaPkg, files) ret, err := cl.NewPackage(prog, ssaPkg, files)
check(err) check(err)

View File

@@ -513,8 +513,6 @@ _llgo_0:
ret ptr %1 ret ptr %1
} }
declare i32 @rand()
define void @"github.com/goplus/llgo/internal/runtime.init"() { define void @"github.com/goplus/llgo/internal/runtime.init"() {
_llgo_0: _llgo_0:
%0 = load i1, ptr @"github.com/goplus/llgo/internal/runtime.init$guard", align 1 %0 = load i1, ptr @"github.com/goplus/llgo/internal/runtime.init$guard", align 1
@@ -620,4 +618,6 @@ declare ptr @memcpy(ptr, ptr, i64)
declare void @"github.com/goplus/llgo/internal/abi.init"() declare void @"github.com/goplus/llgo/internal/abi.init"()
declare i32 @rand()
declare i32 @fprintf(ptr, ptr, ...) declare i32 @fprintf(ptr, ptr, ...)

View File

@@ -17,12 +17,9 @@
package ssa_test package ssa_test
import ( import (
"go/types"
"testing" "testing"
"github.com/goplus/llgo/cl/cltest" "github.com/goplus/llgo/cl/cltest"
"github.com/goplus/llgo/internal/typeutil"
"github.com/goplus/llgo/ssa"
) )
func TestFromTestrt(t *testing.T) { func TestFromTestrt(t *testing.T) {
@@ -38,6 +35,34 @@ func TestRuntime(t *testing.T) {
cltest.Pkg(t, "github.com/goplus/llgo/internal/abi", "../internal/abi/llgo_autogen.ll") cltest.Pkg(t, "github.com/goplus/llgo/internal/abi", "../internal/abi/llgo_autogen.ll")
} }
/*
func TestCallback(t *testing.T) {
ctx := llvm.NewContext()
mod := ctx.NewModule("foo/bar")
tc := llvm.FunctionType(ctx.VoidType(), nil, false)
callback := llvm.PointerType(tc, 0)
params := []llvm.Type{callback}
tfn := llvm.FunctionType(ctx.VoidType(), params, false)
f := llvm.AddFunction(mod, "fn", tfn)
b := ctx.NewBuilder()
blk := llvm.AddBasicBlock(f, "")
b.SetInsertPointAtEnd(blk)
arg := f.Param(0)
// arg = b.CreateLoad(tc, arg, "")
b.CreateCall(tc, arg, nil, "")
b.CreateRetVoid()
expected := `; ModuleID = 'foo/bar'
`
if v := mod.String(); v != expected {
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
}
}
/*
func TestMap(t *testing.T) { func TestMap(t *testing.T) {
var m typeutil.Map var m typeutil.Map
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
@@ -51,3 +76,4 @@ func TestMap(t *testing.T) {
t.Fatal("At(csig):", v) t.Fatal("At(csig):", v)
} }
} }
*/

View File

@@ -25,6 +25,26 @@ import (
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
const (
ClosureCtx = "__llgo_ctx"
ClosureStub = "__llgo_stub."
NameValist = "__llgo_va_list"
)
func VArg() *types.Var {
return types.NewParam(0, nil, NameValist, types.Typ[types.Invalid])
}
func IsVArg(arg *types.Var) bool {
return arg.Name() == NameValist
}
func HasVArg(t *types.Tuple, n int) bool {
return n > 0 && IsVArg(t.At(n-1))
}
// -----------------------------------------------------------------------------
type aNamedConst struct { type aNamedConst struct {
} }
@@ -106,24 +126,31 @@ func (g Global) Init(v Expr) {
// respectively, and is nil in the generic method. // respectively, and is nil in the generic method.
type aFunction struct { type aFunction struct {
Expr Expr
pkg Package Pkg Package
prog Program Prog Program
blks []BasicBlock blks []BasicBlock
params []Type params []Type
freeVars Expr
base int // base = 1 if hasFreeVars; base = 0 otherwise
hasVArg bool hasVArg bool
} }
// Function represents a function or method. // Function represents a function or method.
type Function = *aFunction type Function = *aFunction
func newFunction(fn llvm.Value, t Type, pkg Package, prog Program) Function { func newFunction(fn llvm.Value, t Type, pkg Package, prog Program, hasFreeVars bool) Function {
params, hasVArg := newParams(t, prog) params, hasVArg := newParams(t, prog)
return &aFunction{Expr{fn, t}, pkg, prog, nil, params, hasVArg} base := 0
if hasFreeVars {
base = 1
}
return &aFunction{Expr{fn, t}, pkg, prog, nil, params, Expr{}, base, hasVArg}
} }
func newParams(fn Type, prog Program) (params []Type, hasVArg bool) { func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
sig := fn.t.(*types.Signature) sig := fn.raw.Type.(*types.Signature)
in := sig.Params() in := sig.Params()
if n := in.Len(); n > 0 { if n := in.Len(); n > 0 {
if hasVArg = HasVArg(in, n); hasVArg { if hasVArg = HasVArg(in, n); hasVArg {
@@ -131,7 +158,7 @@ func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
} }
params = make([]Type, n) params = make([]Type, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
params[i] = prog.Type(in.At(i).Type()) params[i] = prog.rawType(in.At(i).Type())
} }
} }
return return
@@ -139,18 +166,41 @@ func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
// Params returns the function's ith parameter. // Params returns the function's ith parameter.
func (p Function) Param(i int) Expr { func (p Function) Param(i int) Expr {
i += p.base // skip if hasFreeVars
return Expr{p.impl.Param(i), p.params[i]} return Expr{p.impl.Param(i), p.params[i]}
} }
func (p Function) closureCtx(b Builder) Expr {
if p.freeVars.IsNil() {
if p.base == 0 {
panic("ssa: function has no free variables")
}
ptr := Expr{p.impl.Param(0), p.params[0]}
p.freeVars = b.Load(ptr)
}
return p.freeVars
}
// FreeVar returns the function's ith free variable.
func (p Function) FreeVar(b Builder, i int) Expr {
ctx := p.closureCtx(b)
return b.getField(ctx, i)
}
// NewBuilder creates a new Builder for the function. // NewBuilder creates a new Builder for the function.
func (p Function) NewBuilder() Builder { func (p Function) NewBuilder() Builder {
prog := p.prog prog := p.Prog
b := prog.ctx.NewBuilder() b := prog.ctx.NewBuilder()
// TODO(xsw): Finalize may cause panic, so comment it. // TODO(xsw): Finalize may cause panic, so comment it.
// b.Finalize() // b.Finalize()
return &aBuilder{b, p, prog} return &aBuilder{b, p, prog}
} }
// HasBody reports whether the function has a body.
func (p Function) HasBody() bool {
return len(p.blks) > 0
}
// MakeBody creates nblk basic blocks for the function, and creates // MakeBody creates nblk basic blocks for the function, and creates
// a new Builder associated to #0 block. // a new Builder associated to #0 block.
func (p Function) MakeBody(nblk int) Builder { func (p Function) MakeBody(nblk int) Builder {

View File

@@ -41,20 +41,13 @@ func (v Expr) IsNil() bool {
return v.Type == nil return v.Type == nil
} }
/*
// 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. // Do evaluates the delay expression and returns the result.
func (v Expr) Do(b Builder) Expr { func (v Expr) Do(b Builder) Expr {
switch vt := v.Type; vt.kind { switch vt := v.Type; vt.kind {
case vkDelayExpr: case vkDelayExpr:
return vt.t.(delayExprTy)() return vt.raw.Type.(delayExprTy)()
case vkPhisExpr: case vkPhisExpr:
e := vt.t.(*phisExprTy) e := vt.raw.Type.(*phisExprTy)
return b.aggregateValue(e.Type, e.phis...) return b.aggregateValue(e.Type, e.phis...)
} }
return v return v
@@ -64,7 +57,7 @@ func (v Expr) Do(b Builder) Expr {
// DelayExpr returns a delay expression. // DelayExpr returns a delay expression.
func DelayExpr(f func() Expr) Expr { func DelayExpr(f func() Expr) Expr {
return Expr{Type: &aType{t: delayExprTy(f), kind: vkDelayExpr}} return Expr{Type: &aType{raw: rawType{delayExprTy(f)}, kind: vkDelayExpr}}
} }
type delayExprTy func() Expr type delayExprTy func() Expr
@@ -93,7 +86,7 @@ func (p phisExprTy) String() string {
} }
func phisExpr(t Type, phis []llvm.Value) Expr { func phisExpr(t Type, phis []llvm.Value) Expr {
return Expr{Type: &aType{t: &phisExprTy{phis, t}, kind: vkPhisExpr}} return Expr{Type: &aType{raw: rawType{&phisExprTy{phis, t}}, kind: vkPhisExpr}}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -148,7 +141,8 @@ func (b Builder) Const(v constant.Value, typ Type) Expr {
if v == nil { if v == nil {
return prog.Null(typ) return prog.Null(typ)
} }
switch t := typ.t.(type) { raw := typ.raw.Type
switch t := raw.(type) {
case *types.Basic: case *types.Basic:
kind := t.Kind() kind := t.Kind()
switch { switch {
@@ -170,7 +164,7 @@ func (b Builder) Const(v constant.Value, typ Type) Expr {
return b.Str(constant.StringVal(v)) return b.Str(constant.StringVal(v))
} }
} }
panic(fmt.Sprintf("unsupported Const: %v, %v", v, typ.t)) panic(fmt.Sprintf("unsupported Const: %v, %v", v, raw))
} }
// SizeOf returns the size of a type. // SizeOf returns the size of a type.
@@ -189,7 +183,7 @@ func (b Builder) CStr(v string) Expr {
func (b Builder) Str(v string) (ret Expr) { func (b Builder) Str(v string) (ret Expr) {
prog := b.Prog prog := b.Prog
cstr := b.CStr(v) cstr := b.CStr(v)
ret = b.InlineCall(b.fn.pkg.rtFunc("NewString"), cstr, prog.Val(len(v))) ret = b.InlineCall(b.Func.Pkg.rtFunc("NewString"), cstr, prog.Val(len(v)))
ret.Type = prog.String() ret.Type = prog.String()
return return
} }
@@ -302,7 +296,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
switch kind { switch kind {
case vkString: case vkString:
if op == token.ADD { if op == token.ADD {
pkg := b.fn.pkg pkg := b.Func.Pkg
return b.InlineCall(pkg.rtFunc("StringCat"), x, y) return b.InlineCall(pkg.rtFunc("StringCat"), x, y)
} }
case vkComplex: case vkComplex:
@@ -362,26 +356,50 @@ func (b Builder) UnOp(op token.Token, x Expr) Expr {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
func checkExpr(v Expr, t types.Type, b Builder) Expr { func checkExpr(v Expr, t types.Type, b Builder) Expr {
if _, ok := t.(*types.Signature); ok { if t, ok := t.(*types.Struct); ok && isClosure(t) {
if v.kind != vkClosure { if v.kind != vkClosure {
prog := b.Prog return b.Func.Pkg.closureStub(b, t, v)
nilVal := prog.Null(prog.VoidPtr()).impl
return b.aggregateValue(prog.Type(t), v.impl, nilVal)
} }
} }
return v return v
} }
func llvmValues(vals []Expr, params *types.Tuple, b Builder) []llvm.Value { func llvmParamsEx(data Expr, vals []Expr, params *types.Tuple, b Builder) (ret []llvm.Value) {
if data.IsNil() {
return llvmParams(0, vals, params, b)
}
ret = llvmParams(1, vals, params, b)
ret[0] = data.impl
return
}
func llvmParams(base int, vals []Expr, params *types.Tuple, b Builder) (ret []llvm.Value) {
n := params.Len() n := params.Len()
ret := make([]llvm.Value, len(vals)) if n > 0 {
for i, v := range vals { ret = make([]llvm.Value, len(vals)+base)
for idx, v := range vals {
i := base + idx
if i < n { if i < n {
v = checkExpr(v, params.At(i).Type(), b) v = checkExpr(v, params.At(i).Type(), b)
} }
ret[i] = v.impl ret[i] = v.impl
} }
return ret }
return
}
func llvmFields(vals []Expr, t *types.Struct, b Builder) (ret []llvm.Value) {
n := t.NumFields()
if n > 0 {
ret = make([]llvm.Value, len(vals))
for i, v := range vals {
if i < n {
v = checkExpr(v, t.Field(i).Type(), b)
}
ret[i] = v.impl
}
}
return
} }
func llvmDelayValues(f func(i int) Expr, n int) []llvm.Value { func llvmDelayValues(f func(i int) Expr, n int) []llvm.Value {
@@ -413,7 +431,7 @@ func (p Phi) AddIncoming(b Builder, bblks []BasicBlock, f func(i int) Expr) {
p.impl.AddIncoming(vs, bs) p.impl.AddIncoming(vs, bs)
return return
} }
e := p.t.(*phisExprTy) e := p.raw.Type.(*phisExprTy)
phis := e.phis phis := e.phis
vals := make([][]llvm.Value, len(phis)) vals := make([][]llvm.Value, len(phis))
for iblk, blk := range bblks { for iblk, blk := range bblks {
@@ -436,7 +454,7 @@ func (p Phi) AddIncoming(b Builder, bblks []BasicBlock, f func(i int) Expr) {
// Phi returns a phi node. // Phi returns a phi node.
func (b Builder) Phi(t Type) Phi { func (b Builder) Phi(t Type) Phi {
impl := b.impl impl := b.impl
switch tund := t.t.Underlying().(type) { switch tund := t.raw.Type.Underlying().(type) {
case *types.Basic: case *types.Basic:
kind := tund.Kind() kind := tund.Kind()
switch kind { switch kind {
@@ -447,6 +465,8 @@ func (b Builder) Phi(t Type) Phi {
phis[1] = llvm.CreatePHI(impl, prog.tyInt()) phis[1] = llvm.CreatePHI(impl, prog.tyInt())
return Phi{phisExpr(t, phis)} return Phi{phisExpr(t, phis)}
} }
case *types.Struct:
panic("todo")
} }
phi := llvm.CreatePHI(impl, t.ll) phi := llvm.CreatePHI(impl, t.ll)
return Phi{Expr{phi, t}} return Phi{Expr{phi, t}}
@@ -474,20 +494,32 @@ func (b Builder) Load(ptr Expr) Expr {
// Store stores val at the pointer ptr. // Store stores val at the pointer ptr.
func (b Builder) Store(ptr, val Expr) Builder { func (b Builder) Store(ptr, val Expr) Builder {
raw := ptr.raw.Type
if debugInstr { if debugInstr {
log.Printf("Store %v, %v\n", ptr.impl, val.impl) log.Printf("Store %v, %v, %v\n", raw, ptr.impl, val.impl)
} }
val = checkExpr(val, raw.(*types.Pointer).Elem(), b)
b.impl.CreateStore(val.impl, ptr.impl) b.impl.CreateStore(val.impl, ptr.impl)
return b return b
} }
func (b Builder) aggregateAlloc(t Type, flds ...llvm.Value) llvm.Value {
prog := b.Prog
pkg := b.Func.Pkg
size := prog.SizeOf(t)
ptr := b.InlineCall(pkg.rtFunc("AllocU"), prog.IntVal(size, prog.Uintptr())).impl
tll := t.ll
impl := b.impl
for i, fld := range flds {
impl.CreateStore(fld, llvm.CreateStructGEP(impl, tll, ptr, i))
}
return ptr
}
// aggregateValue yields the value of the aggregate X with the fields // aggregateValue yields the value of the aggregate X with the fields
func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr { func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr {
if debugInstr {
log.Printf("AggregateValue %v, %v\n", t, flds)
}
impl := b.impl
tll := t.ll tll := t.ll
impl := b.impl
ptr := llvm.CreateAlloca(impl, tll) ptr := llvm.CreateAlloca(impl, tll)
for i, fld := range flds { for i, fld := range flds {
impl.CreateStore(fld, llvm.CreateStructGEP(impl, tll, ptr, i)) impl.CreateStore(fld, llvm.CreateStructGEP(impl, tll, ptr, i))
@@ -495,6 +527,28 @@ func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr {
return Expr{llvm.CreateLoad(b.impl, tll, ptr), t} return Expr{llvm.CreateLoad(b.impl, tll, ptr), t}
} }
// The MakeClosure instruction yields a closure value whose code is
// Fn and whose free variables' values are supplied by Bindings.
//
// Type() returns a (possibly named) *types.Signature.
//
// Example printed form:
//
// t0 = make closure anon@1.2 [x y z]
// t1 = make closure bound$(main.I).add [i]
func (b Builder) MakeClosure(fn Expr, bindings []Expr) Expr {
if debugInstr {
log.Printf("MakeClosure %v, %v\n", fn, bindings)
}
prog := b.Prog
tfn := fn.Type
sig := tfn.raw.Type.(*types.Signature)
tctx := sig.Params().At(0).Type().Underlying().(*types.Pointer).Elem().(*types.Struct)
flds := llvmFields(bindings, tctx, b)
data := b.aggregateAlloc(prog.rawType(tctx), flds...)
return b.aggregateValue(prog.Closure(tfn), fn.impl, data)
}
// The FieldAddr instruction yields the address of Field of *struct X. // The FieldAddr instruction yields the address of Field of *struct X.
// //
// The field is identified by its index within the field list of the // The field is identified by its index within the field list of the
@@ -524,8 +578,13 @@ func (b Builder) Field(x Expr, idx int) Expr {
if debugInstr { if debugInstr {
log.Printf("Field %v, %d\n", x.impl, idx) log.Printf("Field %v, %d\n", x.impl, idx)
} }
telem := b.Prog.Field(x.Type, idx) return b.getField(x, idx)
return Expr{llvm.CreateExtractValue(b.impl, x.impl, idx), telem} }
func (b Builder) getField(x Expr, idx int) Expr {
tfld := b.Prog.Field(x.Type, idx)
fld := llvm.CreateExtractValue(b.impl, x.impl, idx)
return Expr{fld, tfld}
} }
// The IndexAddr instruction yields the address of the element at // The IndexAddr instruction yields the address of the element at
@@ -547,9 +606,9 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
prog := b.Prog prog := b.Prog
telem := prog.Index(x.Type) telem := prog.Index(x.Type)
pt := prog.Pointer(telem) pt := prog.Pointer(telem)
switch x.t.Underlying().(type) { switch x.raw.Type.Underlying().(type) {
case *types.Slice: case *types.Slice:
pkg := b.fn.pkg pkg := b.Func.Pkg
ptr := b.InlineCall(pkg.rtFunc("SliceData"), x) ptr := b.InlineCall(pkg.rtFunc("SliceData"), x)
indices := []llvm.Value{idx.impl} indices := []llvm.Value{idx.impl}
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt} return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
@@ -573,13 +632,13 @@ func (b Builder) Index(x, idx Expr, addr func(Expr) Expr) Expr {
prog := b.Prog prog := b.Prog
var telem Type var telem Type
var ptr Expr var ptr Expr
switch t := x.t.Underlying().(type) { switch t := x.raw.Type.Underlying().(type) {
case *types.Basic: case *types.Basic:
if t.Info()&types.IsString == 0 { if t.Kind() != types.String {
panic(fmt.Errorf("invalid operation: cannot index %v", t)) panic(fmt.Errorf("invalid operation: cannot index %v", t))
} }
telem = prog.Type(types.Typ[types.Byte]) telem = prog.rawType(types.Typ[types.Byte])
pkg := b.fn.pkg pkg := b.Func.Pkg
ptr = b.InlineCall(pkg.rtFunc("StringData"), x) ptr = b.InlineCall(pkg.rtFunc("StringData"), x)
case *types.Array: case *types.Array:
telem = prog.Index(x.Type) telem = prog.Index(x.Type)
@@ -597,6 +656,26 @@ func (b Builder) Index(x, idx Expr, addr func(Expr) Expr) Expr {
return b.Load(buf) return b.Load(buf)
} }
// The Lookup instruction yields element Index of collection map X.
// Index is the appropriate key type.
//
// If CommaOk, the result is a 2-tuple of the value above and a
// boolean indicating the result of a map membership test for the key.
// The components of the tuple are accessed using Extract.
//
// Example printed form:
//
// t2 = t0[t1]
// t5 = t3[t4],ok
func (b Builder) Lookup(x, key Expr, commaOk bool) (ret Expr) {
if debugInstr {
log.Printf("Lookup %v, %v, %v\n", x.impl, key.impl, commaOk)
}
// TODO(xsw)
// panic("todo")
return
}
// The Slice instruction yields a slice of an existing string, slice // The Slice instruction yields a slice of an existing string, slice
// or *array X between optional integer bounds Low and High. // or *array X between optional integer bounds Low and High.
// //
@@ -614,14 +693,14 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
log.Printf("Slice %v, %v, %v\n", x.impl, low.impl, high.impl) log.Printf("Slice %v, %v, %v\n", x.impl, low.impl, high.impl)
} }
prog := b.Prog prog := b.Prog
pkg := b.fn.pkg pkg := b.Func.Pkg
var nCap Expr var nCap Expr
var nEltSize Expr var nEltSize Expr
var base Expr var base Expr
if low.IsNil() { if low.IsNil() {
low = prog.IntVal(0, prog.Int()) low = prog.IntVal(0, prog.Int())
} }
switch t := x.t.Underlying().(type) { switch t := x.raw.Type.Underlying().(type) {
case *types.Basic: case *types.Basic:
if t.Kind() != types.String { if t.Kind() != types.String {
panic(fmt.Errorf("invalid operation: cannot slice %v", t)) panic(fmt.Errorf("invalid operation: cannot slice %v", t))
@@ -644,7 +723,7 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
telem := t.Elem() telem := t.Elem()
switch te := telem.Underlying().(type) { switch te := telem.Underlying().(type) {
case *types.Array: case *types.Array:
elem := prog.Type(te.Elem()) elem := prog.rawType(te.Elem())
ret.Type = prog.Slice(elem) ret.Type = prog.Slice(elem)
nEltSize = b.SizeOf(elem) nEltSize = b.SizeOf(elem)
nCap = prog.IntVal(uint64(te.Len()), prog.Int()) nCap = prog.IntVal(uint64(te.Len()), prog.Int())
@@ -674,9 +753,9 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
// t1 = make StringIntMap t0 // t1 = make StringIntMap t0
func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) { func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("MakeMap %v, %v\n", t, nReserve.impl) log.Printf("MakeMap %v, %v\n", t.RawType(), nReserve.impl)
} }
pkg := b.fn.pkg pkg := b.Func.Pkg
ret.Type = t ret.Type = t
ret.impl = b.InlineCall(pkg.rtFunc("MakeSmallMap")).impl ret.impl = b.InlineCall(pkg.rtFunc("MakeSmallMap")).impl
// TODO(xsw): nReserve // TODO(xsw): nReserve
@@ -699,9 +778,9 @@ func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) {
// t1 = make StringSlice 1:int t0 // t1 = make StringSlice 1:int t0
func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) { func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("MakeSlice %v, %v, %v\n", t, len.impl, cap.impl) log.Printf("MakeSlice %v, %v, %v\n", t.RawType(), len.impl, cap.impl)
} }
pkg := b.fn.pkg pkg := b.Func.Pkg
if cap.IsNil() { if cap.IsNil() {
cap = len cap = len
} }
@@ -734,13 +813,12 @@ func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) {
// //
// t0 = local int // t0 = local int
// t1 = new int // t1 = new int
func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) { func (b Builder) Alloc(elem Type, heap bool) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("Alloc %v, %v\n", t, heap) log.Printf("Alloc %v, %v\n", elem.RawType(), heap)
} }
prog := b.Prog prog := b.Prog
pkg := b.fn.pkg pkg := b.Func.Pkg
elem := prog.Type(t.Elem())
size := b.SizeOf(elem) size := b.SizeOf(elem)
if heap { if heap {
ret = b.InlineCall(pkg.rtFunc("AllocZ"), size) ret = b.InlineCall(pkg.rtFunc("AllocZ"), size)
@@ -748,7 +826,7 @@ func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) {
ret = Expr{llvm.CreateAlloca(b.impl, elem.ll), prog.VoidPtr()} ret = Expr{llvm.CreateAlloca(b.impl, elem.ll), prog.VoidPtr()}
ret.impl = b.InlineCall(pkg.rtFunc("Zeroinit"), ret, size).impl ret.impl = b.InlineCall(pkg.rtFunc("Zeroinit"), ret, size).impl
} }
ret.Type = prog.Type(t) ret.Type = prog.Pointer(elem)
return return
} }
@@ -760,7 +838,7 @@ func (b Builder) Alloca(n Expr) (ret Expr) {
prog := b.Prog prog := b.Prog
telem := prog.tyInt8() telem := prog.tyInt8()
ret.impl = llvm.CreateArrayAlloca(b.impl, telem, n.impl) ret.impl = llvm.CreateArrayAlloca(b.impl, telem, n.impl)
ret.Type = &aType{prog.tyVoidPtr(), types.Typ[types.UnsafePointer], vkPtr} ret.Type = prog.VoidPtr()
return return
} }
@@ -781,7 +859,7 @@ func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("AllocaCStr %v\n", gostr.impl) log.Printf("AllocaCStr %v\n", gostr.impl)
} }
pkg := b.fn.pkg pkg := b.Func.Pkg
n := b.InlineCall(pkg.rtFunc("StringLen"), gostr) n := b.InlineCall(pkg.rtFunc("StringLen"), gostr)
n1 := b.BinOp(token.ADD, n, b.Prog.Val(1)) n1 := b.BinOp(token.ADD, n, b.Prog.Val(1))
cstr := b.Alloca(n1) cstr := b.Alloca(n1)
@@ -815,13 +893,14 @@ func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
// t1 = changetype *int <- IntPtr (t0) // t1 = changetype *int <- IntPtr (t0)
func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("ChangeType %v, %v\n", t.t, x.impl) log.Printf("ChangeType %v, %v\n", t.RawType(), x.impl)
} }
typ := t.t typ := t.raw.Type
switch typ.(type) { switch typ.(type) {
default: default:
// TODO(xsw): remove instr name
ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast") ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast")
ret.Type = b.Prog.Type(typ) ret.Type = b.Prog.rawType(typ)
return return
} }
} }
@@ -853,8 +932,8 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
// //
// t1 = convert []byte <- string (t0) // t1 = convert []byte <- string (t0)
func (b Builder) Convert(t Type, x Expr) (ret Expr) { func (b Builder) Convert(t Type, x Expr) (ret Expr) {
typ := t.t typ := t.raw.Type
ret.Type = b.Prog.Type(typ) ret.Type = b.Prog.rawType(typ)
switch und := typ.Underlying().(type) { switch und := typ.Underlying().(type) {
case *types.Basic: case *types.Basic:
kind := und.Kind() kind := und.Kind()
@@ -905,17 +984,17 @@ func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
// //
// t1 = make interface{} <- int (42:int) // t1 = make interface{} <- int (42:int)
// t2 = make Stringer <- t0 // t2 = make Stringer <- t0
func (b Builder) MakeInterface(inter types.Type, x Expr, mayDelay bool) (ret Expr) { func (b Builder) MakeInterface(tinter Type, x Expr, mayDelay bool) (ret Expr) {
raw := tinter.raw.Type
if debugInstr { if debugInstr {
log.Printf("MakeInterface %v, %v\n", inter, x.impl) log.Printf("MakeInterface %v, %v\n", raw, x.impl)
} }
tiund := inter.Underlying().(*types.Interface) tiund := raw.Underlying().(*types.Interface)
isAny := tiund.Empty() isAny := tiund.Empty()
fnDo := func() Expr { fnDo := func() Expr {
prog := b.Prog prog := b.Prog
pkg := b.fn.pkg pkg := b.Func.Pkg
tinter := prog.Type(inter) switch tx := x.raw.Type.Underlying().(type) {
switch tx := x.t.Underlying().(type) {
case *types.Basic: case *types.Basic:
kind := tx.Kind() kind := tx.Kind()
switch { switch {
@@ -977,18 +1056,18 @@ func (b Builder) MakeInterface(inter types.Type, x Expr, mayDelay bool) (ret Exp
// t3 = typeassert,ok t2.(T) // t3 = typeassert,ok t2.(T)
func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.t, commaOk) log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.raw.Type, commaOk)
} }
switch assertedTyp.kind { switch assertedTyp.kind {
case vkSigned, vkUnsigned, vkFloat: case vkSigned, vkUnsigned, vkFloat:
pkg := b.fn.pkg pkg := b.Func.Pkg
fnName := "I2Int" fnName := "I2Int"
if commaOk { if commaOk {
fnName = "CheckI2Int" fnName = "CheckI2Int"
} }
fn := pkg.rtFunc(fnName) fn := pkg.rtFunc(fnName)
var kind types.BasicKind var kind types.BasicKind
switch t := assertedTyp.t.(type) { switch t := assertedTyp.raw.Type.(type) {
case *types.Basic: case *types.Basic:
kind = t.Kind() kind = t.Kind()
default: default:
@@ -1026,7 +1105,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
if name == "" { if name == "" {
name = "closure" name = "closure"
} }
fmt.Fprint(&b, "Call ", fn.t, " ", name) fmt.Fprint(&b, "Call ", fn.kind, " ", fn.raw.Type, " ", name)
sep := ": " sep := ": "
for _, arg := range args { for _, arg := range args {
fmt.Fprint(&b, sep, arg.impl) fmt.Fprint(&b, sep, arg.impl)
@@ -1034,26 +1113,42 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
} }
log.Println(b.String()) log.Println(b.String())
} }
var ll llvm.Type
var data Expr
var sig *types.Signature var sig *types.Signature
var t = fn.t var raw = fn.raw.Type
normal := true
switch fn.kind { switch fn.kind {
case vkClosure: case vkClosure:
data = b.Field(fn, 1)
fn = b.Field(fn, 0) fn = b.Field(fn, 0)
t = fn.t raw = fn.raw.Type
normal = false
fallthrough fallthrough
case vkFuncDecl, vkFuncPtr: case vkFuncPtr:
sig = t.(*types.Signature) sig = raw.(*types.Signature)
ret.Type = prog.retType(sig) ll = prog.FuncDecl(sig, InC).ll
case vkFuncDecl:
sig = raw.(*types.Signature)
ll = fn.ll
default: default:
panic("unreachable") panic("unreachable")
} }
if normal { ret.Type = prog.retType(sig)
ret.impl = llvm.CreateCall(b.impl, fn.ll, fn.impl, llvmValues(args, sig.Params(), b)) ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b))
} else { return
ret = prog.IntVal(0, prog.Type(types.Typ[types.Int32]))
} }
// The Extract instruction yields component Index of Tuple.
//
// This is used to access the results of instructions with multiple
// return values, such as Call, TypeAssert, Next, UnOp(ARROW) and
// IndexExpr(Map).
//
// Example printed form:
//
// t1 = extract t0 #1
func (b Builder) Extract(x Expr, index int) (ret Expr) {
ret.Type = b.Prog.toType(x.Type.raw.Type.(*types.Tuple).At(index).Type())
ret.impl = b.impl.CreateExtractValue(x.impl, index, "")
return return
} }
@@ -1068,21 +1163,21 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
case "len": case "len":
if len(args) == 1 { if len(args) == 1 {
arg := args[0] arg := args[0]
switch t := arg.t.Underlying().(type) { switch t := arg.raw.Type.Underlying().(type) {
case *types.Slice: case *types.Slice:
return b.InlineCall(b.fn.pkg.rtFunc("SliceLen"), arg) return b.InlineCall(b.Func.Pkg.rtFunc("SliceLen"), arg)
case *types.Basic: case *types.Basic:
if t.Kind() == types.String { if t.Kind() == types.String {
return b.InlineCall(b.fn.pkg.rtFunc("StringLen"), arg) return b.InlineCall(b.Func.Pkg.rtFunc("StringLen"), arg)
} }
} }
} }
case "cap": case "cap":
if len(args) == 1 { if len(args) == 1 {
arg := args[0] arg := args[0]
switch arg.t.Underlying().(type) { switch arg.raw.Type.Underlying().(type) {
case *types.Slice: case *types.Slice:
return b.InlineCall(b.fn.pkg.rtFunc("SliceCap"), arg) return b.InlineCall(b.Func.Pkg.rtFunc("SliceCap"), arg)
} }
} }
} }

View File

@@ -17,10 +17,12 @@
package ssa package ssa
import ( import (
"go/token"
"go/types" "go/types"
"log"
"github.com/goplus/llgo/internal/typeutil"
"github.com/goplus/llvm" "github.com/goplus/llvm"
"golang.org/x/tools/go/types/typeutil"
) )
const ( const (
@@ -95,8 +97,8 @@ func Initialize(flags InitFlags) {
type aProgram struct { type aProgram struct {
ctx llvm.Context ctx llvm.Context
typs typeutil.Map typs typeutil.Map // rawType -> Type
// sizs types.Sizes gocvt goTypes
rt *types.Package rt *types.Package
rtget func() *types.Package rtget func() *types.Package
@@ -152,7 +154,7 @@ func NewProgram(target *Target) Program {
// TODO(xsw): Finalize may cause panic, so comment it. // TODO(xsw): Finalize may cause panic, so comment it.
ctx.Finalize() ctx.Finalize()
*/ */
return &aProgram{ctx: ctx, target: target, td: td} return &aProgram{ctx: ctx, gocvt: newGoTypes(), target: target, td: td}
} }
// SetRuntime sets the runtime. // SetRuntime sets the runtime.
@@ -180,11 +182,13 @@ func (p Program) runtime() *types.Package {
} }
func (p Program) rtNamed(name string) *types.Named { func (p Program) rtNamed(name string) *types.Named {
return p.runtime().Scope().Lookup(name).Type().(*types.Named) t := p.runtime().Scope().Lookup(name).Type().(*types.Named)
t, _ = p.gocvt.cvtNamed(t)
return t
} }
func (p Program) rtType(name string) Type { func (p Program) rtType(name string) Type {
return p.Type(p.rtNamed(name)) return p.rawType(p.rtNamed(name))
} }
func (p Program) rtIface() llvm.Type { func (p Program) rtIface() llvm.Type {
@@ -220,23 +224,24 @@ func (p Program) NewPackage(name, pkgPath string) Package {
mod := p.ctx.NewModule(pkgPath) mod := p.ctx.NewModule(pkgPath)
// TODO(xsw): Finalize may cause panic, so comment it. // TODO(xsw): Finalize may cause panic, so comment it.
// mod.Finalize() // mod.Finalize()
fns := make(map[string]Function)
gbls := make(map[string]Global) gbls := make(map[string]Global)
fns := make(map[string]Function)
stubs := make(map[string]Function)
p.needRuntime = false p.needRuntime = false
return &aPackage{mod, fns, gbls, p} return &aPackage{mod, gbls, fns, stubs, p}
} }
// Void returns void type. // Void returns void type.
func (p Program) Void() Type { func (p Program) Void() Type {
if p.voidTy == nil { if p.voidTy == nil {
p.voidTy = &aType{p.tyVoid(), types.Typ[types.Invalid], vkInvalid} p.voidTy = &aType{p.tyVoid(), rawType{types.Typ[types.Invalid]}, vkInvalid}
} }
return p.voidTy return p.voidTy
} }
func (p Program) VoidPtr() Type { func (p Program) VoidPtr() Type {
if p.voidPtr == nil { if p.voidPtr == nil {
p.voidPtr = p.Type(types.Typ[types.UnsafePointer]) p.voidPtr = p.rawType(types.Typ[types.UnsafePointer])
} }
return p.voidPtr return p.voidPtr
} }
@@ -244,21 +249,21 @@ func (p Program) VoidPtr() Type {
// Bool returns bool type. // Bool returns bool type.
func (p Program) Bool() Type { func (p Program) Bool() Type {
if p.boolTy == nil { if p.boolTy == nil {
p.boolTy = p.Type(types.Typ[types.Bool]) p.boolTy = p.rawType(types.Typ[types.Bool])
} }
return p.boolTy return p.boolTy
} }
func (p Program) CStr() Type { func (p Program) CStr() Type {
if p.cstrTy == nil { // *int8 if p.cstrTy == nil { // *int8
p.cstrTy = p.Type(types.NewPointer(types.Typ[types.Int8])) p.cstrTy = p.rawType(types.NewPointer(types.Typ[types.Int8]))
} }
return p.cstrTy return p.cstrTy
} }
func (p Program) String() Type { func (p Program) String() Type {
if p.stringTy == nil { if p.stringTy == nil {
p.stringTy = p.Type(types.Typ[types.String]) p.stringTy = p.rawType(types.Typ[types.String])
} }
return p.stringTy return p.stringTy
} }
@@ -266,7 +271,7 @@ func (p Program) String() Type {
// Any returns any type. // Any returns any type.
func (p Program) Any() Type { func (p Program) Any() Type {
if p.anyTy == nil { if p.anyTy == nil {
p.anyTy = p.Type(tyAny) p.anyTy = p.rawType(tyAny)
} }
return p.anyTy return p.anyTy
} }
@@ -274,7 +279,7 @@ func (p Program) Any() Type {
// Int returns int type. // Int returns int type.
func (p Program) Int() Type { func (p Program) Int() Type {
if p.intTy == nil { if p.intTy == nil {
p.intTy = p.Type(types.Typ[types.Int]) p.intTy = p.rawType(types.Typ[types.Int])
} }
return p.intTy return p.intTy
} }
@@ -282,7 +287,7 @@ func (p Program) Int() Type {
// Uintptr returns uintptr type. // Uintptr returns uintptr type.
func (p Program) Uintptr() Type { func (p Program) Uintptr() Type {
if p.uintptrTy == nil { if p.uintptrTy == nil {
p.uintptrTy = p.Type(types.Typ[types.Uintptr]) p.uintptrTy = p.rawType(types.Typ[types.Uintptr])
} }
return p.uintptrTy return p.uintptrTy
} }
@@ -290,7 +295,7 @@ func (p Program) Uintptr() Type {
// Float64 returns float64 type. // Float64 returns float64 type.
func (p Program) Float64() Type { func (p Program) Float64() Type {
if p.f64Ty == nil { if p.f64Ty == nil {
p.f64Ty = p.Type(types.Typ[types.Float64]) p.f64Ty = p.rawType(types.Typ[types.Float64])
} }
return p.f64Ty return p.f64Ty
} }
@@ -307,9 +312,10 @@ func (p Program) Float64() Type {
// and unspecified other things too. // and unspecified other things too.
type aPackage struct { type aPackage struct {
mod llvm.Module mod llvm.Module
fns map[string]Function
vars map[string]Global vars map[string]Global
prog Program fns map[string]Function
stubs map[string]Function
Prog Program
} }
type Package = *aPackage type Package = *aPackage
@@ -322,8 +328,8 @@ func (p Package) NewConst(name string, val constant.Value) NamedConst {
*/ */
// NewVar creates a new global variable. // NewVar creates a new global variable.
func (p Package) NewVar(name string, typ types.Type) Global { func (p Package) NewVar(name string, typ types.Type, bg Background) Global {
t := p.prog.Type(typ) t := p.Prog.Type(typ, bg)
gbl := llvm.AddGlobal(p.mod, t.ll, name) gbl := llvm.AddGlobal(p.mod, t.ll, name)
ret := &aGlobal{Expr{gbl, t}} ret := &aGlobal{Expr{gbl, t}}
p.vars[name] = ret p.vars[name] = ret
@@ -336,28 +342,70 @@ func (p Package) VarOf(name string) Global {
} }
// NewFunc creates a new function. // NewFunc creates a new function.
func (p Package) NewFunc(name string, sig *types.Signature) Function { func (p Package) NewFunc(name string, sig *types.Signature, bg Background) Function {
return p.NewFuncEx(name, sig, bg, false)
}
// NewFuncEx creates a new function.
func (p Package) NewFuncEx(name string, sig *types.Signature, bg Background, hasFreeVars bool) Function {
if v, ok := p.fns[name]; ok { if v, ok := p.fns[name]; ok {
return v return v
} }
t := p.prog.llvmFuncDecl(sig) t := p.Prog.FuncDecl(sig, bg)
if debugInstr {
log.Println("NewFunc", name, t.raw.Type, "hasFreeVars:", hasFreeVars)
}
fn := llvm.AddFunction(p.mod, name, t.ll) fn := llvm.AddFunction(p.mod, name, t.ll)
ret := newFunction(fn, t, p, p.prog) ret := newFunction(fn, t, p, p.Prog, hasFreeVars)
p.fns[name] = ret p.fns[name] = ret
return ret return ret
} }
func (p Package) rtFunc(fnName string) Expr {
fn := p.Prog.runtime().Scope().Lookup(fnName).(*types.Func)
name := FullName(fn.Pkg(), fnName)
sig := fn.Type().(*types.Signature)
return p.NewFunc(name, sig, InGo).Expr
}
func (p Package) closureStub(b Builder, t *types.Struct, v Expr) Expr {
name := v.impl.Name()
prog := b.Prog
nilVal := prog.Null(prog.VoidPtr()).impl
if fn, ok := p.stubs[name]; ok {
v = fn.Expr
} else {
sig := v.raw.Type.(*types.Signature)
n := sig.Params().Len()
nret := sig.Results().Len()
ctx := types.NewParam(token.NoPos, nil, ClosureCtx, types.Typ[types.UnsafePointer])
sig = FuncAddCtx(ctx, sig)
fn := p.NewFunc(ClosureStub+name, sig, InC)
fn.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
args := make([]Expr, n)
for i := 0; i < n; i++ {
args[i] = fn.Param(i + 1)
}
b := fn.MakeBody(1)
call := b.Call(v, args...)
call.impl.SetTailCall(true)
switch nret {
case 0:
b.impl.CreateRetVoid()
default: // TODO(xsw): support multiple return values
b.impl.CreateRet(call.impl)
}
p.stubs[name] = fn
v = fn.Expr
}
return b.aggregateValue(prog.rawType(t), v.impl, nilVal)
}
// FuncOf returns a function by name. // FuncOf returns a function by name.
func (p Package) FuncOf(name string) Function { func (p Package) FuncOf(name string) Function {
return p.fns[name] return p.fns[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
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// String returns a string representation of the package. // String returns a string representation of the package.

View File

@@ -25,12 +25,15 @@ import (
"github.com/goplus/llvm" "github.com/goplus/llvm"
) )
/* func TestClosureCtx(t *testing.T) {
func TestMakeInterface(t *testing.T) { defer func() {
var b Builder if r := recover(); r == nil {
b.MakeInterface(types.NewInterfaceType(nil, nil), Expr{}, true).Do(true) t.Log("closureCtx: no error?")
}
}()
var f aFunction
f.closureCtx(nil)
} }
*/
func TestTypes(t *testing.T) { func TestTypes(t *testing.T) {
ctx := llvm.NewContext() ctx := llvm.NewContext()
@@ -52,30 +55,21 @@ func TestIndexType(t *testing.T) {
indexType(types.Typ[types.Int]) indexType(types.Typ[types.Int])
} }
func TestCvtCType(t *testing.T) { func TestCvtType(t *testing.T) {
test := func(typ types.Type) { gt := newGoTypes()
callback := types.NewSignatureType(nil, nil, nil, nil, nil, false)
params := types.NewTuple(types.NewParam(0, nil, "", callback))
sig := types.NewSignatureType(nil, nil, nil, params, nil, false)
ret1 := gt.cvtFunc(sig, nil)
if ret1 == sig {
t.Fatal("cvtFunc failed")
}
defer func() { defer func() {
if r := recover(); r == nil { if r := recover(); r == nil {
t.Log("cvtCType: no error?") t.Log("cvtType: no error?")
} }
}() }()
cvtCType(typ) gt.cvtType(nil)
}
test(types.NewInterfaceType(nil, nil))
a := types.NewTypeName(0, nil, "a", nil)
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
named := types.NewNamed(a, sig, nil)
test(named)
}
func TestCFuncPtr(t *testing.T) {
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
csig := (*CFuncPtr)(sig)
_ = csig.String()
if csig.Underlying() != sig {
t.Fatal("TestCFuncPtr failed")
}
} }
func TestUserdefExpr(t *testing.T) { func TestUserdefExpr(t *testing.T) {
@@ -118,9 +112,9 @@ func assertPkg(t *testing.T, p Package, expected string) {
func TestVar(t *testing.T) { func TestVar(t *testing.T) {
prog := NewProgram(nil) prog := NewProgram(nil)
pkg := prog.NewPackage("bar", "foo/bar") pkg := prog.NewPackage("bar", "foo/bar")
a := pkg.NewVar("a", types.Typ[types.Int]) a := pkg.NewVar("a", types.Typ[types.Int], InGo)
a.Init(prog.Val(100)) a.Init(prog.Val(100))
b := pkg.NewVar("b", types.Typ[types.Int]) b := pkg.NewVar("b", types.Typ[types.Int], InGo)
b.Init(a.Expr) b.Init(a.Expr)
assertPkg(t, pkg, `; ModuleID = 'foo/bar' assertPkg(t, pkg, `; ModuleID = 'foo/bar'
source_filename = "foo/bar" source_filename = "foo/bar"
@@ -135,7 +129,7 @@ func TestConst(t *testing.T) {
pkg := prog.NewPackage("bar", "foo/bar") pkg := prog.NewPackage("bar", "foo/bar")
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Bool])) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Bool]))
sig := types.NewSignatureType(nil, nil, nil, nil, rets, false) sig := types.NewSignatureType(nil, nil, nil, nil, rets, false)
b := pkg.NewFunc("fn", sig).MakeBody(1) b := pkg.NewFunc("fn", sig, InGo).MakeBody(1)
b.Return(b.Const(constant.MakeBool(true), prog.Bool())) b.Return(b.Const(constant.MakeBool(true), prog.Bool()))
assertPkg(t, pkg, `; ModuleID = 'foo/bar' assertPkg(t, pkg, `; ModuleID = 'foo/bar'
source_filename = "foo/bar" source_filename = "foo/bar"
@@ -152,7 +146,7 @@ func TestStruct(t *testing.T) {
prog := NewProgram(nil) prog := NewProgram(nil)
pkg := prog.NewPackage("bar", "foo/bar") pkg := prog.NewPackage("bar", "foo/bar")
pkg.NewVar("a", empty) pkg.NewVar("a", empty, InGo)
assertPkg(t, pkg, `; ModuleID = 'foo/bar' assertPkg(t, pkg, `; ModuleID = 'foo/bar'
source_filename = "foo/bar" source_filename = "foo/bar"
@@ -169,7 +163,7 @@ func TestNamedStruct(t *testing.T) {
prog := NewProgram(nil) prog := NewProgram(nil)
pkg := prog.NewPackage("bar", "foo/bar") pkg := prog.NewPackage("bar", "foo/bar")
pkg.NewVar("a", empty) pkg.NewVar("a", empty, InGo)
if pkg.VarOf("a") == nil { if pkg.VarOf("a") == nil {
t.Fatal("VarOf failed") t.Fatal("VarOf failed")
} }
@@ -187,7 +181,7 @@ func TestDeclFunc(t *testing.T) {
pkg := prog.NewPackage("bar", "foo/bar") pkg := prog.NewPackage("bar", "foo/bar")
params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int])) params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, params, nil, false) sig := types.NewSignatureType(nil, nil, nil, params, nil, false)
pkg.NewFunc("fn", sig) pkg.NewFunc("fn", sig, InGo)
if pkg.FuncOf("fn") == nil { if pkg.FuncOf("fn") == nil {
t.Fatal("FuncOf failed") t.Fatal("FuncOf failed")
} }
@@ -209,7 +203,7 @@ func TestBasicFunc(t *testing.T) {
types.NewVar(0, nil, "b", types.Typ[types.Float64])) types.NewVar(0, nil, "b", types.Typ[types.Float64]))
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, params, rets, false) sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
pkg.NewFunc("fn", sig).MakeBody(1). pkg.NewFunc("fn", sig, InGo).MakeBody(1).
Return(prog.Val(1)) Return(prog.Val(1))
assertPkg(t, pkg, `; ModuleID = 'foo/bar' assertPkg(t, pkg, `; ModuleID = 'foo/bar'
source_filename = "foo/bar" source_filename = "foo/bar"
@@ -229,7 +223,7 @@ func TestFuncParam(t *testing.T) {
types.NewVar(0, nil, "b", types.Typ[types.Float64])) types.NewVar(0, nil, "b", types.Typ[types.Float64]))
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, params, rets, false) sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
fn := pkg.NewFunc("fn", sig) fn := pkg.NewFunc("fn", sig, InGo)
fn.MakeBody(1).Return(fn.Param(0)) fn.MakeBody(1).Return(fn.Param(0))
assertPkg(t, pkg, `; ModuleID = 'foo/bar' assertPkg(t, pkg, `; ModuleID = 'foo/bar'
source_filename = "foo/bar" source_filename = "foo/bar"
@@ -250,12 +244,12 @@ func TestFuncCall(t *testing.T) {
types.NewVar(0, nil, "b", types.Typ[types.Float64])) types.NewVar(0, nil, "b", types.Typ[types.Float64]))
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, params, rets, false) sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
fn := pkg.NewFunc("fn", sig) fn := pkg.NewFunc("fn", sig, InGo)
fn.MakeBody(1). fn.MakeBody(1).
Return(prog.Val(1)) Return(prog.Val(1))
sigMain := types.NewSignatureType(nil, nil, nil, nil, nil, false) sigMain := types.NewSignatureType(nil, nil, nil, nil, nil, false)
b := pkg.NewFunc("main", sigMain).MakeBody(1) b := pkg.NewFunc("main", sigMain, InGo).MakeBody(1)
b.Call(fn.Expr, prog.Val(1), prog.Val(1.2)) b.Call(fn.Expr, prog.Val(1), prog.Val(1.2))
b.Return() b.Return()
@@ -284,8 +278,8 @@ func TestFuncMultiRet(t *testing.T) {
types.NewVar(0, nil, "c", types.Typ[types.Int]), types.NewVar(0, nil, "c", types.Typ[types.Int]),
types.NewVar(0, nil, "d", types.Typ[types.Float64])) types.NewVar(0, nil, "d", types.Typ[types.Float64]))
sig := types.NewSignatureType(nil, nil, nil, params, rets, false) sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
a := pkg.NewVar("a", types.Typ[types.Int]) a := pkg.NewVar("a", types.Typ[types.Int], InGo)
fn := pkg.NewFunc("fn", sig) fn := pkg.NewFunc("fn", sig, InGo)
b := fn.MakeBody(1) b := fn.MakeBody(1)
b.Return(a.Expr, fn.Param(0)) b.Return(a.Expr, fn.Param(0))
assertPkg(t, pkg, `; ModuleID = 'foo/bar' assertPkg(t, pkg, `; ModuleID = 'foo/bar'
@@ -305,7 +299,7 @@ func TestJump(t *testing.T) {
prog := NewProgram(nil) prog := NewProgram(nil)
pkg := prog.NewPackage("bar", "foo/bar") pkg := prog.NewPackage("bar", "foo/bar")
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
fn := pkg.NewFunc("loop", sig) fn := pkg.NewFunc("loop", sig, InGo)
b := fn.MakeBody(1) b := fn.MakeBody(1)
b.Jump(fn.Block(0)) b.Jump(fn.Block(0))
assertPkg(t, pkg, `; ModuleID = 'foo/bar' assertPkg(t, pkg, `; ModuleID = 'foo/bar'
@@ -324,7 +318,7 @@ func TestIf(t *testing.T) {
params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int])) params := types.NewTuple(types.NewVar(0, nil, "a", types.Typ[types.Int]))
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, params, rets, false) sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
fn := pkg.NewFunc("fn", sig) fn := pkg.NewFunc("fn", sig, InGo)
b := fn.MakeBody(3) b := fn.MakeBody(3)
iftrue := fn.Block(1) iftrue := fn.Block(1)
iffalse := fn.Block(2) iffalse := fn.Block(2)
@@ -359,7 +353,7 @@ func TestPrintf(t *testing.T) {
params := types.NewTuple(types.NewVar(0, nil, "format", pchar), VArg()) params := types.NewTuple(types.NewVar(0, nil, "format", pchar), VArg())
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int32])) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int32]))
sig := types.NewSignatureType(nil, nil, nil, params, rets, false) sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
pkg.NewFunc("printf", sig) pkg.NewFunc("printf", sig, InGo)
assertPkg(t, pkg, `; ModuleID = 'foo/bar' assertPkg(t, pkg, `; ModuleID = 'foo/bar'
source_filename = "foo/bar" source_filename = "foo/bar"
@@ -375,7 +369,7 @@ func TestBinOp(t *testing.T) {
types.NewVar(0, nil, "b", types.Typ[types.Float64])) types.NewVar(0, nil, "b", types.Typ[types.Float64]))
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, params, rets, false) sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
fn := pkg.NewFunc("fn", sig) fn := pkg.NewFunc("fn", sig, InGo)
b := fn.MakeBody(1) b := fn.MakeBody(1)
ret := b.BinOp(token.ADD, fn.Param(0), prog.Val(1)) ret := b.BinOp(token.ADD, fn.Param(0), prog.Val(1))
b.Return(ret) b.Return(ret)
@@ -398,7 +392,7 @@ func TestUnOp(t *testing.T) {
) )
rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int]))
sig := types.NewSignatureType(nil, nil, nil, params, rets, false) sig := types.NewSignatureType(nil, nil, nil, params, rets, false)
fn := pkg.NewFunc("fn", sig) fn := pkg.NewFunc("fn", sig, InGo)
b := fn.MakeBody(1) b := fn.MakeBody(1)
ptr := fn.Param(0) ptr := fn.Param(0)
val := b.UnOp(token.MUL, ptr) val := b.UnOp(token.MUL, ptr)

View File

@@ -50,7 +50,7 @@ func (p BasicBlock) Index() int {
type aBuilder struct { type aBuilder struct {
impl llvm.Builder impl llvm.Builder
fn Function Func Function
Prog Program Prog Program
} }
@@ -59,7 +59,7 @@ type Builder = *aBuilder
// SetBlock sets the current block to the specified basic block. // SetBlock sets the current block to the specified basic block.
func (b Builder) SetBlock(blk BasicBlock) Builder { func (b Builder) SetBlock(blk BasicBlock) Builder {
if b.fn != blk.fn { if b.Func != blk.fn {
panic("mismatched function") panic("mismatched function")
} }
if debugInstr { if debugInstr {
@@ -74,7 +74,7 @@ func (b Builder) Panic(v Expr) {
if debugInstr { if debugInstr {
log.Printf("Panic %v\n", v.impl) log.Printf("Panic %v\n", v.impl)
} }
pkg := b.fn.pkg pkg := b.Func.Pkg
b.Call(pkg.rtFunc("TracePanic"), v) b.Call(pkg.rtFunc("TracePanic"), v)
b.impl.CreateUnreachable() b.impl.CreateUnreachable()
} }
@@ -101,16 +101,18 @@ func (b Builder) Return(results ...Expr) {
case 0: case 0:
b.impl.CreateRetVoid() b.impl.CreateRetVoid()
case 1: case 1:
b.impl.CreateRet(results[0].impl) raw := b.Func.raw.Type.(*types.Signature).Results().At(0).Type()
ret := checkExpr(results[0], raw, b)
b.impl.CreateRet(ret.impl)
default: default:
tret := b.fn.t.(*types.Signature).Results() tret := b.Func.raw.Type.(*types.Signature).Results()
b.impl.CreateAggregateRet(llvmValues(results, tret, b)) b.impl.CreateAggregateRet(llvmParams(0, results, tret, b))
} }
} }
// Jump emits a jump instruction. // Jump emits a jump instruction.
func (b Builder) Jump(jmpb BasicBlock) { func (b Builder) Jump(jmpb BasicBlock) {
if b.fn != jmpb.fn { if b.Func != jmpb.fn {
panic("mismatched function") panic("mismatched function")
} }
if debugInstr { if debugInstr {
@@ -121,7 +123,7 @@ func (b Builder) Jump(jmpb BasicBlock) {
// If emits an if instruction. // If emits an if instruction.
func (b Builder) If(cond Expr, thenb, elseb BasicBlock) { func (b Builder) If(cond Expr, thenb, elseb BasicBlock) {
if b.fn != thenb.fn || b.fn != elseb.fn { if b.Func != thenb.fn || b.Func != elseb.fn {
panic("mismatched function") panic("mismatched function")
} }
if debugInstr { if debugInstr {

View File

@@ -18,7 +18,6 @@ package ssa
import ( import (
"fmt" "fmt"
"go/token"
"go/types" "go/types"
"github.com/goplus/llvm" "github.com/goplus/llvm"
@@ -41,9 +40,9 @@ const (
vkString vkString
vkBool vkBool
vkPtr vkPtr
vkFuncDecl // func decl vkFuncDecl
vkFuncPtr // func ptr in C vkFuncPtr
vkClosure // func ptr in Go vkClosure
vkTuple vkTuple
vkDelayExpr = -1 vkDelayExpr = -1
vkPhisExpr = -2 vkPhisExpr = -2
@@ -66,32 +65,25 @@ func indexType(t types.Type) types.Type {
panic("index: type doesn't support index - " + t.String()) 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 rawType struct {
types.Type
}
type aType struct { type aType struct {
ll llvm.Type ll llvm.Type
t types.Type raw rawType
kind valueKind // value kind of llvm.Type kind valueKind // value kind of llvm.Type
} }
type Type = *aType type Type = *aType
// RawType returns the raw type.
func (t Type) RawType() types.Type {
return t.raw.Type
}
// TODO(xsw): // TODO(xsw):
// how to generate platform independent code? // how to generate platform independent code?
func (p Program) SizeOf(typ Type, n ...int64) uint64 { func (p Program) SizeOf(typ Type, n ...int64) uint64 {
@@ -103,42 +95,37 @@ func (p Program) SizeOf(typ Type, n ...int64) uint64 {
} }
func (p Program) Slice(typ Type) Type { func (p Program) Slice(typ Type) Type {
return p.Type(types.NewSlice(typ.t)) return p.rawType(types.NewSlice(typ.raw.Type))
} }
func (p Program) Pointer(typ Type) Type { func (p Program) Pointer(typ Type) Type {
return p.Type(types.NewPointer(typ.t)) return p.rawType(types.NewPointer(typ.raw.Type))
} }
func (p Program) Elem(typ Type) Type { func (p Program) Elem(typ Type) Type {
elem := typ.t.(*types.Pointer).Elem() elem := typ.raw.Type.(*types.Pointer).Elem()
return p.Type(elem) return p.rawType(elem)
} }
func (p Program) Index(typ Type) Type { func (p Program) Index(typ Type) Type {
return p.Type(indexType(typ.t)) return p.rawType(indexType(typ.raw.Type))
} }
func (p Program) Field(typ Type, i int) Type { func (p Program) Field(typ Type, i int) Type {
tunder := typ.t.Underlying() tunder := typ.raw.Type.Underlying()
tfld := tunder.(*types.Struct).Field(i).Type() tfld := tunder.(*types.Struct).Field(i).Type()
return p.Type(tfld) return p.rawType(tfld)
} }
func (p Program) Type(typ types.Type) Type { func (p Program) rawType(raw types.Type) Type {
if v := p.typs.At(typ); v != nil { if v := p.typs.At(raw); v != nil {
return v.(Type) return v.(Type)
} }
ret := p.toLLVMType(typ) ret := p.toType(raw)
p.typs.Set(typ, ret) p.typs.Set(raw, ret)
return ret return ret
} }
func (p Program) llvmFuncDecl(sig *types.Signature) Type {
sig = methodToFunc(sig)
return p.toLLVMFunc(sig, false, true) // don't save func decl to cache
}
func (p Program) tyVoidPtr() llvm.Type { func (p Program) tyVoidPtr() llvm.Type {
if p.voidPtrTy.IsNil() { if p.voidPtrTy.IsNil() {
p.voidPtrTy = llvm.PointerType(p.tyVoid(), 0) p.voidPtrTy = llvm.PointerType(p.tyVoid(), 0)
@@ -202,8 +189,9 @@ func (p Program) tyInt64() llvm.Type {
return p.int64Type return p.int64Type
} }
func (p Program) toLLVMType(typ types.Type) Type { func (p Program) toType(raw types.Type) Type {
switch t := typ.(type) { typ := rawType{raw}
switch t := raw.(type) {
case *types.Basic: case *types.Basic:
switch t.Kind() { switch t.Kind() {
case types.Int: case types.Int:
@@ -240,7 +228,7 @@ func (p Program) toLLVMType(typ types.Type) Type {
return &aType{p.tyVoidPtr(), typ, vkPtr} return &aType{p.tyVoidPtr(), typ, vkPtr}
} }
case *types.Pointer: case *types.Pointer:
elem := p.Type(t.Elem()) elem := p.rawType(t.Elem())
return &aType{llvm.PointerType(elem.ll, 0), typ, vkPtr} return &aType{llvm.PointerType(elem.ll, 0), typ, vkPtr}
case *types.Interface: case *types.Interface:
return &aType{p.rtIface(), typ, vkInvalid} return &aType{p.rtIface(), typ, vkInvalid}
@@ -249,39 +237,52 @@ func (p Program) toLLVMType(typ types.Type) Type {
case *types.Map: case *types.Map:
return &aType{p.rtMap(), typ, vkInvalid} return &aType{p.rtMap(), typ, vkInvalid}
case *types.Struct: case *types.Struct:
return p.toLLVMStruct(t) ll, kind := p.toLLVMStruct(t)
return &aType{ll, typ, kind}
case *types.Named: case *types.Named:
return p.toLLVMNamed(t) return p.toNamed(t)
case *types.Signature: case *types.Signature: // represents a C function pointer in raw type
return p.toLLVMFunc(t, false, false) return &aType{p.toLLVMFuncPtr(t), typ, vkFuncPtr}
case *CFuncPtr:
return p.toLLVMFunc((*types.Signature)(t), true, false)
case *types.Array: case *types.Array:
elem := p.Type(t.Elem()) elem := p.rawType(t.Elem())
return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid} return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid}
case *types.Chan: case *types.Chan:
} }
panic(fmt.Sprintf("toLLVMType: todo - %T\n", typ)) panic(fmt.Sprintf("toLLVMType: todo - %T\n", typ))
} }
func (p Program) toLLVMNamedStruct(name string, typ *types.Struct) llvm.Type { func (p Program) toLLVMNamedStruct(name string, raw *types.Struct) llvm.Type {
t := p.ctx.StructCreateNamed(name) t := p.ctx.StructCreateNamed(name)
fields := p.toLLVMFields(typ) fields := p.toLLVMFields(raw)
t.StructSetBody(fields, false) t.StructSetBody(fields, false)
return t return t
} }
func (p Program) toLLVMStruct(typ *types.Struct) Type { func (p Program) toLLVMStruct(raw *types.Struct) (ret llvm.Type, kind valueKind) {
fields := p.toLLVMFields(typ) fields := p.toLLVMFields(raw)
return &aType{p.ctx.StructType(fields, false), typ, vkInvalid} ret = p.ctx.StructType(fields, false)
if isClosure(raw) {
kind = vkClosure
}
return
} }
func (p Program) toLLVMFields(typ *types.Struct) (fields []llvm.Type) { func isClosure(raw *types.Struct) bool {
n := typ.NumFields() n := raw.NumFields()
if n == 2 {
if _, ok := raw.Field(0).Type().(*types.Signature); ok {
return raw.Field(1).Type() == types.Typ[types.UnsafePointer]
}
}
return false
}
func (p Program) toLLVMFields(raw *types.Struct) (fields []llvm.Type) {
n := raw.NumFields()
if n > 0 { if n > 0 {
fields = make([]llvm.Type, n) fields = make([]llvm.Type, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
fields[i] = p.Type(typ.Field(i).Type()).ll fields[i] = p.rawType(raw.Field(i).Type()).ll
} }
} }
return return
@@ -295,14 +296,13 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) {
if n > 0 { if n > 0 {
ret = make([]llvm.Type, n) ret = make([]llvm.Type, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
ret[i] = p.Type(t.At(i).Type()).ll ret[i] = p.rawType(t.At(i).Type()).ll
} }
} }
return return
} }
func (p Program) toLLVMFunc(sig *types.Signature, inC, isDecl bool) Type { func (p Program) toLLVMFunc(sig *types.Signature) llvm.Type {
if isDecl || inC {
tParams := sig.Params() tParams := sig.Params()
n := tParams.Len() n := tParams.Len()
hasVArg := HasVArg(tParams, n) hasVArg := HasVArg(tParams, n)
@@ -312,52 +312,41 @@ func (p Program) toLLVMFunc(sig *types.Signature, inC, isDecl bool) Type {
params := p.toLLVMTypes(tParams, n) params := p.toLLVMTypes(tParams, n)
out := sig.Results() out := sig.Results()
var ret llvm.Type var ret llvm.Type
var kind valueKind
switch nret := out.Len(); nret { switch nret := out.Len(); nret {
case 0: case 0:
ret = p.tyVoid() ret = p.tyVoid()
case 1: case 1:
ret = p.Type(out.At(0).Type()).ll ret = p.rawType(out.At(0).Type()).ll
default: default:
ret = p.toLLVMTuple(out) ret = p.toLLVMTuple(out)
} }
ft := llvm.FunctionType(ret, params, hasVArg) return llvm.FunctionType(ret, params, hasVArg)
if isDecl {
kind = vkFuncDecl
} else {
ft = llvm.PointerType(ft, 0)
kind = vkFuncPtr
}
return &aType{ft, sig, kind}
}
flds := []*types.Var{
types.NewField(token.NoPos, nil, "f", (*CFuncPtr)(sig), false),
types.NewField(token.NoPos, nil, "data", types.Typ[types.UnsafePointer], false),
}
t := types.NewStruct(flds, nil)
ll := p.ctx.StructType(p.toLLVMFields(t), false)
return &aType{ll, t, vkClosure}
} }
func (p Program) retType(sig *types.Signature) Type { func (p Program) toLLVMFuncPtr(sig *types.Signature) llvm.Type {
out := sig.Results() ft := p.toLLVMFunc(sig)
return llvm.PointerType(ft, 0)
}
func (p Program) retType(raw *types.Signature) Type {
out := raw.Results()
switch n := out.Len(); n { switch n := out.Len(); n {
case 0: case 0:
return p.Void() return p.Void()
case 1: case 1:
return p.Type(out.At(0).Type()) return p.rawType(out.At(0).Type())
default: default:
return &aType{p.toLLVMTuple(out), out, vkTuple} return &aType{p.toLLVMTuple(out), rawType{out}, vkTuple}
} }
} }
func (p Program) toLLVMNamed(typ *types.Named) Type { func (p Program) toNamed(raw *types.Named) Type {
switch t := typ.Underlying().(type) { switch t := raw.Underlying().(type) {
case *types.Struct: case *types.Struct:
name := NameOf(typ) name := NameOf(raw)
return &aType{p.toLLVMNamedStruct(name, t), typ, vkInvalid} return &aType{p.toLLVMNamedStruct(name, t), rawType{raw}, vkInvalid}
default: default:
return p.Type(t) return p.rawType(t)
} }
} }

View File

@@ -1,140 +0,0 @@
/*
* 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
import (
"go/types"
"github.com/goplus/llgo/internal/typeutil"
)
// -----------------------------------------------------------------------------
const (
NameValist = "__llgo_va_list"
)
func VArg() *types.Var {
return types.NewParam(0, nil, NameValist, types.Typ[types.Invalid])
}
func IsVArg(arg *types.Var) bool {
return arg.Name() == NameValist
}
func HasVArg(t *types.Tuple, n int) bool {
return n > 0 && IsVArg(t.At(n-1))
}
// -----------------------------------------------------------------------------
// CFuncPtr represents a C function pointer.
type CFuncPtr types.Signature
func (t *CFuncPtr) String() string { return (*types.Signature)(t).String() }
func (t *CFuncPtr) Underlying() types.Type { return (*types.Signature)(t) }
func (t *CFuncPtr) Hash(h typeutil.Hasher) uint32 {
return typeutil.HashSig(h, (*types.Signature)(t))*13 + 97
}
// -----------------------------------------------------------------------------
// CType convert a C type into Go.
func CType(typ types.Type) types.Type {
t, _ := cvtCType(typ)
return t
}
// CFuncDecl convert a C function decl into Go signature.
func CFuncDecl(sig *types.Signature) *types.Signature {
hasVArg := sig.Variadic()
params, cvt1 := cvtTuple(sig.Params(), hasVArg)
results, cvt2 := cvtTuple(sig.Results(), false)
if cvt1 || cvt2 {
return types.NewSignatureType(nil, nil, nil, params, results, hasVArg)
}
return sig
}
func cvtCType(typ types.Type) (types.Type, bool) {
switch t := typ.(type) {
case *types.Basic:
case *types.Pointer:
if elem, cvt := cvtCType(t.Elem()); cvt {
return types.NewPointer(elem), true
}
case *types.Struct:
return cvtCStruct(t)
case *types.Named:
if _, cvt := cvtCType(t.Underlying()); cvt {
panic("don't define named type")
}
case *types.Signature:
t = CFuncDecl(t)
return (*CFuncPtr)(t), true
case *types.Array:
if elem, cvt := cvtCType(t.Elem()); cvt {
return types.NewArray(elem, t.Len()), true
}
default:
panic("unreachable")
}
return typ, false
}
func cvtTuple(t *types.Tuple, hasVArg bool) (*types.Tuple, bool) {
n := t.Len()
vars := make([]*types.Var, n)
needcvt := false
if hasVArg {
n--
vars[n] = t.At(n)
}
for i := 0; i < n; i++ {
v := t.At(i)
if t, cvt := cvtCType(v.Type()); cvt {
v = types.NewParam(v.Pos(), v.Pkg(), v.Name(), t)
needcvt = true
}
vars[i] = v
}
if needcvt {
return types.NewTuple(vars...), true
}
return t, false
}
func cvtCStruct(typ *types.Struct) (*types.Struct, bool) {
n := typ.NumFields()
flds := make([]*types.Var, n)
needcvt := false
for i := 0; i < n; i++ {
f := typ.Field(i)
if t, cvt := cvtCType(f.Type()); cvt {
f = types.NewField(f.Pos(), f.Pkg(), f.Name(), t, f.Anonymous())
needcvt = true
}
flds[i] = f
}
if needcvt {
return types.NewStruct(flds, nil), true
}
return typ, false
}
// -----------------------------------------------------------------------------

254
ssa/type_cvt.go Normal file
View File

@@ -0,0 +1,254 @@
/*
* 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
import (
"go/token"
"go/types"
"unsafe"
)
// -----------------------------------------------------------------------------
type goTypes struct {
typs map[unsafe.Pointer]unsafe.Pointer
}
func newGoTypes() goTypes {
typs := make(map[unsafe.Pointer]unsafe.Pointer)
return goTypes{typs}
}
type Background int
const (
inUnknown Background = iota
InGo
InC
)
// Type convert a Go/C type into raw type.
// C type = raw type
// Go type: convert to raw type (because of closure)
func (p Program) Type(typ types.Type, bg Background) Type {
if bg == InGo {
typ, _ = p.gocvt.cvtType(typ)
}
return p.rawType(typ)
}
// FuncDecl converts a Go/C function declaration into raw type.
func (p Program) FuncDecl(sig *types.Signature, bg Background) Type {
if bg == InGo {
sig = p.gocvt.cvtFunc(sig, sig.Recv())
}
return &aType{p.toLLVMFunc(sig), rawType{sig}, vkFuncDecl}
}
// Closure creates a closture type for a function.
func (p Program) Closure(fn Type) Type {
sig := fn.raw.Type.(*types.Signature)
closure := p.gocvt.cvtClosure(sig)
return p.rawType(closure)
}
func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {
switch t := typ.(type) {
case *types.Basic:
case *types.Pointer:
if elem, cvt := p.cvtType(t.Elem()); cvt {
return types.NewPointer(elem), true
}
case *types.Interface:
return p.cvtInterface(t)
case *types.Slice:
if elem, cvt := p.cvtType(t.Elem()); cvt {
return types.NewSlice(elem), true
}
case *types.Map:
key, cvt1 := p.cvtType(t.Key())
elem, cvt2 := p.cvtType(t.Elem())
if cvt1 || cvt2 {
return types.NewMap(key, elem), true
}
case *types.Struct:
return p.cvtStruct(t)
case *types.Named:
return p.cvtNamed(t)
case *types.Signature:
return p.cvtClosure(t), true
case *types.Array:
if elem, cvt := p.cvtType(t.Elem()); cvt {
return types.NewArray(elem, t.Len()), true
}
case *types.Chan:
if elem, cvt := p.cvtType(t.Elem()); cvt {
return types.NewChan(t.Dir(), elem), true
}
default:
panic("unreachable")
}
return typ, false
}
func (p goTypes) cvtNamed(t *types.Named) (raw *types.Named, cvt bool) {
if v, ok := p.typs[unsafe.Pointer(t)]; ok {
raw = (*types.Named)(v)
cvt = t != raw
return
}
defer func() {
p.typs[unsafe.Pointer(t)] = unsafe.Pointer(raw)
}()
if tund, cvt := p.cvtType(t.Underlying()); cvt {
old := t.Obj()
obj := types.NewTypeName(old.Pos(), old.Pkg(), old.Name(), nil)
return types.NewNamed(obj, tund, nil), true
}
return t, false
}
func (p goTypes) cvtClosure(sig *types.Signature) *types.Struct {
ctx := types.NewParam(token.NoPos, nil, ClosureCtx, types.Typ[types.UnsafePointer])
raw := p.cvtFunc(sig, ctx)
flds := []*types.Var{
types.NewField(token.NoPos, nil, "f", raw, false),
types.NewField(token.NoPos, nil, "data", types.Typ[types.UnsafePointer], false),
}
return types.NewStruct(flds, nil)
}
func (p goTypes) cvtFunc(sig *types.Signature, recv *types.Var) (raw *types.Signature) {
if recv != nil {
sig = FuncAddCtx(recv, sig)
}
params, cvt1 := p.cvtTuple(sig.Params())
results, cvt2 := p.cvtTuple(sig.Results())
if cvt1 || cvt2 {
return types.NewSignatureType(nil, nil, nil, params, results, sig.Variadic())
}
return sig
}
func (p goTypes) cvtTuple(t *types.Tuple) (*types.Tuple, bool) {
n := t.Len()
vars := make([]*types.Var, n)
needcvt := false
for i := 0; i < n; i++ {
v := t.At(i)
if t, cvt := p.cvtType(v.Type()); cvt {
v = types.NewParam(v.Pos(), v.Pkg(), v.Name(), t)
needcvt = true
}
vars[i] = v
}
if needcvt {
return types.NewTuple(vars...), true
}
return t, false
}
func (p goTypes) cvtExplicitMethods(typ *types.Interface) ([]*types.Func, bool) {
n := typ.NumExplicitMethods()
methods := make([]*types.Func, n)
needcvt := false
for i := 0; i < n; i++ {
m := typ.ExplicitMethod(i)
sig := m.Type().(*types.Signature)
if raw := p.cvtFunc(sig, nil); sig != raw {
m = types.NewFunc(m.Pos(), m.Pkg(), m.Name(), raw)
needcvt = true
}
methods[i] = m
}
return methods, needcvt
}
func (p goTypes) cvtEmbeddedTypes(typ *types.Interface) ([]types.Type, bool) {
n := typ.NumEmbeddeds()
embeddeds := make([]types.Type, n)
needcvt := false
for i := 0; i < n; i++ {
t := typ.EmbeddedType(i)
if raw, cvt := p.cvtType(t); cvt {
t = raw
needcvt = true
}
embeddeds[i] = t
}
return embeddeds, needcvt
}
func (p goTypes) cvtInterface(typ *types.Interface) (raw *types.Interface, cvt bool) {
if v, ok := p.typs[unsafe.Pointer(typ)]; ok {
raw = (*types.Interface)(v)
cvt = typ != raw
return
}
defer func() {
p.typs[unsafe.Pointer(typ)] = unsafe.Pointer(raw)
}()
methods, cvt1 := p.cvtExplicitMethods(typ)
embeddeds, cvt2 := p.cvtEmbeddedTypes(typ)
if cvt1 || cvt2 {
return types.NewInterfaceType(methods, embeddeds), true
}
return typ, false
}
func (p goTypes) cvtStruct(typ *types.Struct) (raw *types.Struct, cvt bool) {
if v, ok := p.typs[unsafe.Pointer(typ)]; ok {
raw = (*types.Struct)(v)
cvt = typ != raw
return
}
defer func() {
p.typs[unsafe.Pointer(typ)] = unsafe.Pointer(raw)
}()
n := typ.NumFields()
flds := make([]*types.Var, n)
needcvt := false
for i := 0; i < n; i++ {
f := typ.Field(i)
if t, cvt := p.cvtType(f.Type()); cvt {
f = types.NewField(f.Pos(), f.Pkg(), f.Name(), t, f.Anonymous())
needcvt = true
}
flds[i] = f
}
if needcvt {
return types.NewStruct(flds, nil), true
}
return typ, false
}
// -----------------------------------------------------------------------------
// FuncAddCtx adds a ctx to a function signature.
func FuncAddCtx(ctx *types.Var, sig *types.Signature) *types.Signature {
tParams := sig.Params()
nParams := tParams.Len()
params := make([]*types.Var, nParams+1)
params[0] = ctx
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())
}
// -----------------------------------------------------------------------------

262
x/clang/ast/ast.go Normal file
View File

@@ -0,0 +1,262 @@
/*
* Copyright (c) 2022 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 ast
// -----------------------------------------------------------------------------
type IncludedFrom struct {
File string `json:"file"`
}
type Loc struct {
Offset int64 `json:"offset,omitempty"` // 432
File string `json:"file,omitempty"` // "sqlite3.i"
Line int `json:"line,omitempty"`
PresumedFile string `json:"presumedFile,omitempty"`
PresumedLine int `json:"presumedLine,omitempty"`
Col int `json:"col,omitempty"`
TokLen int `json:"tokLen,omitempty"`
IncludedFrom *IncludedFrom `json:"includedFrom,omitempty"` // "sqlite3.c"
}
type Pos struct {
Offset int64 `json:"offset,omitempty"`
Col int `json:"col,omitempty"`
TokLen int `json:"tokLen,omitempty"`
IncludedFrom *IncludedFrom `json:"includedFrom,omitempty"` // "sqlite3.c"
SpellingLoc *Loc `json:"spellingLoc,omitempty"`
ExpansionLoc *Loc `json:"expansionLoc,omitempty"`
}
type Range struct {
Begin Pos `json:"begin"`
End Pos `json:"end"`
}
// -----------------------------------------------------------------------------
type ID string
type Kind string
const (
TranslationUnitDecl Kind = "TranslationUnitDecl"
TypedefType Kind = "TypedefType"
TypedefDecl Kind = "TypedefDecl"
ElaboratedType Kind = "ElaboratedType"
BuiltinType Kind = "BuiltinType"
ConstantArrayType Kind = "ConstantArrayType"
IncompleteArrayType Kind = "IncompleteArrayType"
PointerType Kind = "PointerType"
RecordType Kind = "RecordType"
RecordDecl Kind = "RecordDecl"
FieldDecl Kind = "FieldDecl"
IndirectFieldDecl Kind = "IndirectFieldDecl"
VarDecl Kind = "VarDecl"
EmptyDecl Kind = "EmptyDecl"
EnumDecl Kind = "EnumDecl"
EnumConstantDecl Kind = "EnumConstantDecl"
AlwaysInlineAttr Kind = "AlwaysInlineAttr"
AsmLabelAttr Kind = "AsmLabelAttr"
AvailabilityAttr Kind = "AvailabilityAttr"
DeprecatedAttr Kind = "DeprecatedAttr"
BuiltinAttr Kind = "BuiltinAttr"
FormatAttr Kind = "FormatAttr"
FormatArgAttr Kind = "FormatArgAttr"
ColdAttr Kind = "ColdAttr"
ConstAttr Kind = "ConstAttr"
PureAttr Kind = "PureAttr"
PackedAttr Kind = "PackedAttr"
GNUInlineAttr Kind = "GNUInlineAttr"
StrictFPAttr Kind = "StrictFPAttr"
ReturnsTwiceAttr Kind = "ReturnsTwiceAttr"
RestrictAttr Kind = "RestrictAttr"
NoThrowAttr Kind = "NoThrowAttr"
NoInlineAttr Kind = "NoInlineAttr"
NoSanitizeAttr Kind = "NoSanitizeAttr"
NonNullAttr Kind = "NonNullAttr"
MayAliasAttr Kind = "MayAliasAttr"
MSAllocatorAttr Kind = "MSAllocatorAttr"
MaxFieldAlignmentAttr Kind = "MaxFieldAlignmentAttr"
WarnUnusedResultAttr Kind = "WarnUnusedResultAttr"
AllocSizeAttr Kind = "AllocSizeAttr"
AlignedAttr Kind = "AlignedAttr"
VisibilityAttr Kind = "VisibilityAttr"
C11NoReturnAttr Kind = "C11NoReturnAttr"
FunctionProtoType Kind = "FunctionProtoType"
FunctionDecl Kind = "FunctionDecl"
ParmVarDecl Kind = "ParmVarDecl"
ParenType Kind = "ParenType"
DeclStmt Kind = "DeclStmt"
CompoundStmt Kind = "CompoundStmt"
NullStmt Kind = "NullStmt"
ForStmt Kind = "ForStmt"
WhileStmt Kind = "WhileStmt"
DoStmt Kind = "DoStmt"
GotoStmt Kind = "GotoStmt"
BreakStmt Kind = "BreakStmt"
ContinueStmt Kind = "ContinueStmt"
LabelStmt Kind = "LabelStmt"
IfStmt Kind = "IfStmt"
SwitchStmt Kind = "SwitchStmt"
CaseStmt Kind = "CaseStmt"
DefaultStmt Kind = "DefaultStmt"
ReturnStmt Kind = "ReturnStmt"
GCCAsmStmt Kind = "GCCAsmStmt"
ParenExpr Kind = "ParenExpr"
CallExpr Kind = "CallExpr"
ConstantExpr Kind = "ConstantExpr"
InitListExpr Kind = "InitListExpr"
CStyleCastExpr Kind = "CStyleCastExpr"
DeclRefExpr Kind = "DeclRefExpr"
MemberExpr Kind = "MemberExpr"
ImplicitCastExpr Kind = "ImplicitCastExpr"
ImplicitValueInitExpr Kind = "ImplicitValueInitExpr"
UnaryExprOrTypeTraitExpr Kind = "UnaryExprOrTypeTraitExpr"
OffsetOfExpr Kind = "OffsetOfExpr"
ArraySubscriptExpr Kind = "ArraySubscriptExpr"
AtomicExpr Kind = "AtomicExpr"
VAArgExpr Kind = "VAArgExpr"
CompoundAssignOperator Kind = "CompoundAssignOperator"
BinaryOperator Kind = "BinaryOperator"
UnaryOperator Kind = "UnaryOperator"
ConditionalOperator Kind = "ConditionalOperator"
CompoundLiteralExpr Kind = "CompoundLiteralExpr"
PredefinedExpr Kind = "PredefinedExpr"
CharacterLiteral Kind = "CharacterLiteral"
IntegerLiteral Kind = "IntegerLiteral"
StringLiteral Kind = "StringLiteral"
FloatingLiteral Kind = "FloatingLiteral"
ImaginaryLiteral Kind = "ImaginaryLiteral"
AllocAlignAttr Kind = "AllocAlignAttr"
DisableTailCallsAttr Kind = "DisableTailCallsAttr"
StaticAssertDecl Kind = "StaticAssertDecl"
)
type ValueCategory string
const (
RValue ValueCategory = "rvalue"
PRValue ValueCategory = "prvalue"
LValue ValueCategory = "lvalue"
)
type CC string
const (
CDecl CC = "cdecl"
)
type StorageClass string
const (
Static StorageClass = "static"
Extern StorageClass = "extern"
)
type CastKind string
const (
LValueToRValue CastKind = "LValueToRValue"
BitCast CastKind = "BitCast"
FloatingToIntegral CastKind = "FloatingToIntegral"
FloatingComplexCast CastKind = "FloatingComplexCast"
FloatingRealToComplex CastKind = "FloatingRealToComplex"
IntegralRealToComplex CastKind = "IntegralRealToComplex"
FloatingCast CastKind = "FloatingCast"
IntegralCast CastKind = "IntegralCast"
IntegralToPointer CastKind = "IntegralToPointer"
IntegralToFloating CastKind = "IntegralToFloating"
IntegralToBoolean CastKind = "IntegralToBoolean"
FloatingToBoolean CastKind = "FloatingToBoolean"
IntegralComplexToBoolean CastKind = "IntegralComplexToBoolean"
FloatingComplexToBoolean CastKind = "FloatingComplexToBoolean"
PointerToBoolean CastKind = "PointerToBoolean"
PointerToIntegral CastKind = "PointerToIntegral"
FunctionToPointerDecay CastKind = "FunctionToPointerDecay"
ArrayToPointerDecay CastKind = "ArrayToPointerDecay"
BuiltinFnToFnPtr CastKind = "BuiltinFnToFnPtr"
ToVoid CastKind = "ToVoid"
NullToPointer CastKind = "NullToPointer"
NoOp CastKind = "NoOp"
)
type (
// OpCode can be:
// + - * / || >= -- ++ etc
OpCode string
)
type Type struct {
// QualType can be:
// unsigned int
// struct ConstantString
// volatile uint32_t
// int (*)(void *, int, char **, char **)
// int (*)(const char *, ...)
// int (*)(void)
// const char *restrict
// const char [7]
// char *
// void
// ...
QualType string `json:"qualType"`
DesugaredQualType string `json:"desugaredQualType,omitempty"`
TypeAliasDeclID ID `json:"typeAliasDeclId,omitempty"`
}
type Node struct {
ID ID `json:"id,omitempty"`
Kind Kind `json:"kind,omitempty"`
Loc *Loc `json:"loc,omitempty"`
Range *Range `json:"range,omitempty"`
ReferencedMemberDecl ID `json:"referencedMemberDecl,omitempty"`
PreviousDecl ID `json:"previousDecl,omitempty"`
ParentDeclContextID ID `json:"parentDeclContextId,omitempty"`
IsImplicit bool `json:"isImplicit,omitempty"` // is this type implicit defined
IsReferenced bool `json:"isReferenced,omitempty"` // is this type refered or not
IsUsed bool `json:"isUsed,omitempty"` // is this variable used or not
IsArrow bool `json:"isArrow,omitempty"` // is ptr->member not obj.member
IsPostfix bool `json:"isPostfix,omitempty"`
IsPartOfExplicitCast bool `json:"isPartOfExplicitCast,omitempty"`
IsBitfield bool `json:"isBitfield,omitempty"`
Inline bool `json:"inline,omitempty"`
StorageClass StorageClass `json:"storageClass,omitempty"`
TagUsed string `json:"tagUsed,omitempty"` // struct | union
HasElse bool `json:"hasElse,omitempty"`
CompleteDefinition bool `json:"completeDefinition,omitempty"`
Complicated bool `json:"-"` // complicated statement
Variadic bool `json:"variadic,omitempty"`
Name string `json:"name,omitempty"`
MangledName string `json:"mangledName,omitempty"`
Type *Type `json:"type,omitempty"`
CC CC `json:"cc,omitempty"`
Field *Node `json:"field,omitempty"`
Decl *Node `json:"decl,omitempty"`
OwnedTagDecl *Node `json:"ownedTagDecl,omitempty"`
ReferencedDecl *Node `json:"referencedDecl,omitempty"`
OpCode OpCode `json:"opcode,omitempty"`
Init string `json:"init,omitempty"`
ValueCategory ValueCategory `json:"valueCategory,omitempty"`
Value interface{} `json:"value,omitempty"`
CastKind CastKind `json:"castKind,omitempty"`
Size int `json:"size,omitempty"` // array size
Inner []*Node `json:"inner,omitempty"`
ArrayFiller []*Node `json:"array_filler,omitempty"`
}
// -----------------------------------------------------------------------------

View File

@@ -24,7 +24,7 @@ import (
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Cmd represents a nm command. // Cmd represents a clang command.
type Cmd struct { type Cmd struct {
app string app string
@@ -32,7 +32,7 @@ type Cmd struct {
Stderr io.Writer Stderr io.Writer
} }
// New creates a new nm command. // New creates a new clang command.
func New(app string) *Cmd { func New(app string) *Cmd {
if app == "" { if app == "" {
app = "clang" app = "clang"

122
x/clang/parser/pages.go Normal file
View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 2022 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 parser
// -----------------------------------------------------------------------------
const pageSize = 1024 * 1024
type PagedWriter struct {
pages []*[pageSize]byte
last *[pageSize]byte
off int
}
func NewPagedWriter() *PagedWriter {
return &PagedWriter{last: new([pageSize]byte)}
}
func (p *PagedWriter) Write(buf []byte) (written int, err error) {
for {
n := copy(p.last[p.off:], buf[written:])
written += n
if written >= len(buf) {
p.off += n
return
}
p.pages = append(p.pages, p.last)
p.last, p.off = new([pageSize]byte), 0
}
}
func (p *PagedWriter) Len() int {
return len(p.pages)*pageSize + p.off
}
func (p *PagedWriter) Bytes() []byte {
out, n := make([]byte, p.Len()), 0
for _, page := range p.pages {
n += copy(out[n:], page[:])
}
copy(out[n:], p.last[:p.off])
return out
}
/*
func (p *PagedWriter) ToReader() *PagedReader {
return &PagedReader{src: p, curr: p.getPage(0)}
}
func (p *PagedWriter) getPage(ipage int) []byte {
if ipage == len(p.pages) { // last page
return p.last[:p.off]
}
return p.pages[ipage][:]
}
// -----------------------------------------------------------------------------
type PagedReader struct {
src *PagedWriter
curr []byte
off int
ipage int
}
func (p *PagedReader) WriteTo(w io.Writer) (written int64, err error) {
n, err := w.Write(p.curr[p.off:])
written = int64(n)
if err != nil {
return
}
src, ipage := p.src, p.ipage
for {
if ipage == len(src.pages) { // last page
p.ipage, p.off = ipage, len(p.curr)
return
}
ipage++
page := src.getPage(ipage)
n, err = w.Write(page)
written += int64(n)
if err != nil {
p.ipage, p.curr, p.off = ipage, page, n
return
}
}
}
func (p *PagedReader) Read(buf []byte) (nread int, err error) {
for {
n := copy(buf[nread:], p.curr[p.off:])
nread += n
p.off += n
if nread >= len(buf) {
return
}
src := p.src
if p.ipage == len(src.pages) { // last page
err = io.EOF
return
}
p.ipage++
p.curr, p.off = src.getPage(p.ipage), 0
}
}
*/
// -----------------------------------------------------------------------------

104
x/clang/parser/parse.go Normal file
View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) 2022 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 parser
import (
"bytes"
"os"
"os/exec"
"strings"
"github.com/goplus/llgo/x/clang/ast"
jsoniter "github.com/json-iterator/go"
)
type Mode uint
// -----------------------------------------------------------------------------
type ParseError struct {
Err error
Stderr []byte
}
func (p *ParseError) Error() string {
if len(p.Stderr) > 0 {
return string(p.Stderr)
}
return p.Err.Error()
}
// -----------------------------------------------------------------------------
type Config struct {
Json *[]byte
Flags []string
Stderr bool
}
func DumpAST(filename string, conf *Config) (result []byte, warning []byte, err error) {
if conf == nil {
conf = new(Config)
}
skiperr := strings.HasSuffix(filename, "vfprintf.c.i")
stdout := NewPagedWriter()
stderr := new(bytes.Buffer)
args := []string{"-Xclang", "-ast-dump=json", "-fsyntax-only", filename}
if len(conf.Flags) != 0 {
args = append(conf.Flags, args...)
}
cmd := exec.Command("clang", args...)
cmd.Stdin = os.Stdin
cmd.Stdout = stdout
if conf.Stderr && !skiperr {
cmd.Stderr = os.Stderr
} else {
cmd.Stderr = stderr
}
err = cmd.Run()
errmsg := stderr.Bytes()
if err != nil && !skiperr {
return nil, nil, &ParseError{Err: err, Stderr: errmsg}
}
return stdout.Bytes(), errmsg, nil
}
// -----------------------------------------------------------------------------
var json = jsoniter.ConfigCompatibleWithStandardLibrary
func ParseFileEx(filename string, mode Mode, conf *Config) (file *ast.Node, warning []byte, err error) {
out, warning, err := DumpAST(filename, conf)
if err != nil {
return
}
if conf != nil && conf.Json != nil {
*conf.Json = out
}
file = new(ast.Node)
err = json.Unmarshal(out, file)
if err != nil {
err = &ParseError{Err: err}
}
return
}
func ParseFile(filename string, mode Mode) (file *ast.Node, warning []byte, err error) {
return ParseFileEx(filename, mode, nil)
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2022 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 pathutil
import (
"path/filepath"
)
func Canonical(baseDir string, uri string) string {
if filepath.IsAbs(uri) {
return filepath.Clean(uri)
}
return filepath.Join(baseDir, uri)
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) 2022 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 preprocessor
import (
"log"
"os"
"os/exec"
"path/filepath"
"github.com/goplus/llgo/x/clang/pathutil"
)
const (
DbgFlagExecCmd = 1 << iota
DbgFlagAll = DbgFlagExecCmd
)
var (
debugExecCmd bool
)
func SetDebug(flags int) {
debugExecCmd = (flags & DbgFlagExecCmd) != 0
}
// -----------------------------------------------------------------------------
type Config struct {
Compiler string // default: clang
PPFlag string // default: -E
BaseDir string // base of include searching directory, should be absolute path
IncludeDirs []string
Defines []string
Flags []string
}
func Do(infile, outfile string, conf *Config) (err error) {
if infile, err = filepath.Abs(infile); err != nil {
return
}
if outfile, err = filepath.Abs(outfile); err != nil {
return
}
if conf == nil {
conf = new(Config)
}
base := conf.BaseDir
if base == "" {
if base, err = os.Getwd(); err != nil {
return
}
}
compiler := conf.Compiler
if compiler == "" {
compiler = "clang"
}
ppflag := conf.PPFlag
if ppflag == "" {
ppflag = "-E"
}
n := 4 + len(conf.Flags) + len(conf.IncludeDirs) + len(conf.Defines)
args := make([]string, 3, n)
args[0] = ppflag
args[1], args[2] = "-o", outfile
args = append(args, conf.Flags...)
for _, def := range conf.Defines {
args = append(args, "-D"+def)
}
for _, inc := range conf.IncludeDirs {
args = append(args, "-I"+pathutil.Canonical(base, inc))
}
args = append(args, infile)
if debugExecCmd {
log.Println("==> runCmd:", compiler, args)
}
cmd := exec.Command(compiler, args...)
cmd.Dir = filepath.Dir(infile)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,251 @@
/*
* Copyright (c) 2022 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 parser
import (
"go/token"
"go/types"
"testing"
ctypes "github.com/goplus/llgo/x/clang/types"
)
// -----------------------------------------------------------------------------
var (
pkg = types.NewPackage("", "foo")
scope = pkg.Scope()
)
var (
nameInt128 = types.NewTypeName(token.NoPos, pkg, "__int128", nil)
nameUint128 = types.NewTypeName(token.NoPos, pkg, "__uint128", nil)
tyInt128 = types.NewNamed(nameInt128, types.Typ[types.String], nil)
tyUint128 = types.NewNamed(nameUint128, types.Typ[types.Rune], nil)
)
func init() {
aliasType(scope, pkg, "char", types.Typ[types.Int8])
aliasType(scope, pkg, "void", ctypes.Void)
aliasType(scope, pkg, "float", types.Typ[types.Float32])
aliasType(scope, pkg, "double", types.Typ[types.Float64])
aliasType(scope, pkg, "uint", types.Typ[types.Uint32])
aliasType(scope, pkg, ctypes.MangledName("struct", "ConstantString"), tyConstantString)
aliasType(scope, pkg, ctypes.MangledName("union", "arg"), tyArg)
aliasType(scope, pkg, "va_list", ctypes.Valist)
scope.Insert(nameInt128)
}
func aliasType(scope *types.Scope, pkg *types.Package, name string, typ types.Type) {
o := types.NewTypeName(token.NoPos, pkg, name, typ)
scope.Insert(o)
}
var (
tnameConstantString = types.NewTypeName(token.NoPos, pkg, "ConstantString", nil)
tnameArg = types.NewTypeName(token.NoPos, pkg, "UnionArg", nil)
)
var (
tyChar = types.Typ[types.Int8]
tyUchar = types.Typ[types.Uint8]
tyInt16 = types.Typ[types.Int16]
tyUint16 = types.Typ[types.Uint16]
tyInt32 = types.Typ[types.Int32]
tyUint32 = types.Typ[types.Uint32]
tyInt64 = types.Typ[types.Int64]
tyUint64 = types.Typ[types.Uint64]
tyInt = ctypes.Int
tyInt100 = types.NewArray(tyInt, 100)
tyInt3 = types.NewArray(tyInt, 3)
tyInt3_100 = types.NewArray(tyInt3, 100)
tyPInt3_100 = types.NewPointer(tyInt3_100)
tyPInt100 = types.NewPointer(tyInt100)
tyUint = ctypes.Uint
tyString = types.Typ[types.String]
tyCharPtr = types.NewPointer(tyChar)
tyCharPtrPtr = types.NewPointer(tyCharPtr)
tyConstantString = types.NewNamed(tnameConstantString, tyString, nil)
tyArg = types.NewNamed(tnameArg, tyString, nil)
tyEmptyInterface = types.NewInterfaceType(nil, nil)
)
var (
paramInt = types.NewParam(token.NoPos, pkg, "", tyInt)
paramVoidPtr = types.NewParam(token.NoPos, pkg, "", ctypes.UnsafePointer)
paramCharPtrPtr = types.NewParam(token.NoPos, pkg, "", tyCharPtrPtr)
paramAnySlice = types.NewParam(token.NoPos, pkg, "", types.NewSlice(tyEmptyInterface))
paramPAnySlice = types.NewParam(token.NoPos, pkg, "", types.NewPointer(types.NewSlice(tyEmptyInterface)))
)
var (
typesInt = types.NewTuple(paramInt)
typesIntVA = types.NewTuple(paramInt, paramAnySlice)
typesIntPVA = types.NewTuple(paramInt, paramPAnySlice)
typesVoidPtr = types.NewTuple(paramVoidPtr)
typesPICC = types.NewTuple(paramVoidPtr, paramInt, paramCharPtrPtr, paramCharPtrPtr)
)
func newFn(in, out *types.Tuple) types.Type {
return types.NewSignature(nil, in, out, false)
}
func newFnv(in, out *types.Tuple) types.Type {
return types.NewSignature(nil, in, out, true)
}
func newFnProto(in, out *types.Tuple, variadic bool) types.Type {
return ctypes.NewFunc(in, out, variadic)
}
var (
tyFnHandle = newFn(typesInt, nil)
paramFnHandle = types.NewParam(token.NoPos, pkg, "", tyFnHandle)
typesIF = types.NewTuple(paramInt, paramFnHandle)
typesF = types.NewTuple(paramFnHandle)
)
// -----------------------------------------------------------------------------
type testCase struct {
qualType string
flags int
anonym types.Type
typ types.Type
err string
}
var cases = []testCase{
{qualType: "int", typ: tyInt},
{qualType: "unsigned int", typ: tyUint},
{qualType: "struct ConstantString", typ: tyConstantString},
{qualType: "union arg", typ: tyArg},
{qualType: "volatile signed int", typ: tyInt},
{qualType: "__int128", typ: tyInt128},
{qualType: "signed", typ: tyInt},
{qualType: "signed short", typ: tyInt16},
{qualType: "signed long", typ: ctypes.Long},
{qualType: "unsigned", typ: tyUint},
{qualType: "uint", typ: tyUint32},
{qualType: "unsigned char", typ: tyUchar},
{qualType: "unsigned __int128", typ: tyUint128},
{qualType: "unsigned long", typ: ctypes.Ulong},
{qualType: "unsigned long long", typ: tyUint64},
{qualType: "long double", typ: ctypes.LongDouble},
{qualType: "_Complex float", typ: types.Typ[types.Complex64]},
{qualType: "_Complex double", typ: types.Typ[types.Complex128]},
{qualType: "_Complex long double", typ: types.Typ[types.Complex128]},
{qualType: "int (*)(void)", typ: newFn(nil, typesInt)},
{qualType: "int (void)", typ: newFnProto(nil, typesInt, false)},
{qualType: "void (void) __attribute__((noreturn))", typ: newFnProto(nil, nil, false)},
{qualType: "void (*)(void *)", typ: newFn(typesVoidPtr, nil)},
{qualType: "void (^ _Nonnull)(void)", typ: newFn(nil, nil)},
{qualType: "void (int, ...)", typ: newFnProto(typesIntVA, nil, true)},
{qualType: "void (int, va_list*)", typ: newFn(typesIntPVA, nil)},
{qualType: "va_list *", typ: types.NewPointer(types.NewSlice(tyEmptyInterface))},
{qualType: "int (*)()", typ: newFn(nil, typesInt)},
{qualType: "int (*)(int, ...)", typ: newFnv(typesIntVA, typesInt)},
{qualType: "int (*)(int, struct __va_list_tag*)", typ: newFn(typesIntVA, typesInt)},
{qualType: "int (*volatile)(int, struct __va_list_tag* restrict)", typ: newFn(typesIntVA, typesInt)},
{qualType: "int (const char *, const char *, unsigned int)", flags: FlagGetRetType, typ: tyInt},
{qualType: "const char *restrict", typ: tyCharPtr},
{qualType: "const char [7]", typ: types.NewArray(tyChar, 7)},
{qualType: "const char [7]", flags: FlagIsParam, typ: tyCharPtr},
{qualType: "char []", flags: FlagIsStructField, typ: types.NewArray(tyChar, 0)},
{qualType: "char []", flags: FlagIsExtern, typ: types.NewArray(tyChar, -1)},
{qualType: "char []", flags: 0, err: emsgDefArrWithoutLen},
{qualType: "char []", flags: FlagIsTypedef, typ: types.NewArray(tyChar, -1)},
{qualType: "char []", flags: FlagIsParam, typ: tyCharPtr},
{qualType: "int [100][3]", typ: tyInt3_100},
{qualType: "int (*)[100][3]", typ: tyPInt3_100},
{qualType: "int (*)[100]", typ: tyPInt100},
{qualType: "int (*const [2])(void *)", typ: types.NewArray(newFn(typesVoidPtr, typesInt), 2)},
{qualType: "char *", typ: tyCharPtr},
{qualType: "void", typ: ctypes.Void},
{qualType: "void *", typ: ctypes.UnsafePointer},
{qualType: "int (*_Nullable)(void *, int, char **, char **)", typ: newFn(typesPICC, typesInt)},
{qualType: "void (*(*)(int, void (*)(int)))(int)", typ: newFn(typesIF, typesF)},
{qualType: "void (*(int, void (*)(int)))(int)", typ: newFnProto(typesIF, typesF, false)},
{qualType: "void (*(int, void (*)(int)))(int)", flags: FlagGetRetType, typ: tyFnHandle},
{qualType: "int (*)(void *, int, const char *, void (**)(void *, int, void **), void **)"},
{qualType: "struct (anonymous) [2]", anonym: tyInt, typ: types.NewArray(tyInt, 2)},
{qualType: "enum a", typ: ctypes.Int},
}
type baseEnv struct {
pkg *types.Package
tyInt128 types.Type
tyUint128 types.Type
}
func (p *baseEnv) Pkg() *types.Package {
return p.pkg
}
func (p *baseEnv) Int128() types.Type {
return p.tyInt128
}
func (p *baseEnv) Uint128() types.Type {
return p.tyUint128
}
func TestCases(t *testing.T) {
sel := ""
for _, c := range cases {
if sel != "" && c.qualType != sel {
continue
}
t.Run(c.qualType, func(t *testing.T) {
conf := &Config{
Scope: scope, Flags: c.flags, Anonym: c.anonym,
ParseEnv: &baseEnv{pkg: pkg, tyInt128: tyInt128, tyUint128: tyUint128},
}
typ, _, err := ParseType(c.qualType, conf)
if err != nil {
if errMsgOf(err) != c.err {
t.Fatal("ParseType:", err, ", expected:", c.err)
}
} else if c.typ != nil && !ctypes.Identical(typ, c.typ) {
t.Fatal("ParseType:", typ, ", expected:", c.typ)
}
})
}
}
func errMsgOf(err error) string {
if e, ok := err.(*ParseTypeError); ok {
return e.ErrMsg
}
return err.Error()
}
// -----------------------------------------------------------------------------
func TestIsArrayWithoutLen(t *testing.T) {
_, _, err := ParseType("byte[]", &Config{Scope: types.Universe})
if !IsArrayWithoutLen(err) {
t.Fatal("ParseType:", err)
}
_, _, err = ParseType("byte[]", &Config{Scope: types.Universe, Flags: FlagIsExtern})
if IsArrayWithoutLen(err) {
t.Fatal("ParseType:", err)
}
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,597 @@
/*
* Copyright (c) 2022 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 parser
import (
"errors"
"fmt"
"go/token"
"go/types"
"io"
"log"
"strconv"
"github.com/goplus/gogen"
"github.com/goplus/llgo/x/clang/types/scanner"
ctypes "github.com/goplus/llgo/x/clang/types"
)
const (
emsgDefArrWithoutLen = "define array without length"
)
var (
ErrInvalidType = errors.New("invalid type")
)
type TypeNotFound struct {
Literal string
StructOrUnion bool
}
func (p *TypeNotFound) Error() string {
return fmt.Sprintf("type %s not found", p.Literal)
}
type ParseTypeError struct {
QualType string
ErrMsg string
}
func (p *ParseTypeError) Error() string {
return p.ErrMsg // TODO
}
func IsArrayWithoutLen(err error) bool {
if e, ok := err.(*ParseTypeError); ok {
return e.ErrMsg == emsgDefArrWithoutLen
}
return false
}
// -----------------------------------------------------------------------------
const (
FlagIsParam = 1 << iota
FlagIsStructField
FlagIsExtern
FlagIsTypedef
FlagGetRetType
)
func getRetType(flags int) bool {
return (flags & FlagGetRetType) != 0
}
type ParseEnv interface {
Pkg() *types.Package
Int128() types.Type
Uint128() types.Type
}
type Config struct {
ParseEnv
Scope *types.Scope
Anonym types.Type
Flags int
}
const (
KindFConst = 1 << iota
KindFVolatile
KindFAnonymous
KindFVariadic
)
// qualType can be:
// - unsigned int
// - struct ConstantString
// - volatile uint32_t
// - int (*)(void *, int, char **, char **)
// - int (*)(const char *, ...)
// - int (*)(void)
// - void (*(int, void (*)(int)))(int)
// - const char *restrict
// - const char [7]
// - char *
// - void
// - ...
func ParseType(qualType string, conf *Config) (t types.Type, kind int, err error) {
p := newParser(qualType, conf)
if t, kind, err = p.parse(conf.Flags); err != nil {
return
}
if p.tok != token.EOF {
err = p.newError("unexpect token " + p.tok.String())
}
return
}
// -----------------------------------------------------------------------------
type parser struct {
s scanner.Scanner
scope *types.Scope
conf *Config
tok token.Token
lit string
old struct {
tok token.Token
lit string
}
}
const (
invalidTok token.Token = -1
)
func newParser(qualType string, conf *Config) *parser {
p := &parser{scope: conf.Scope, conf: conf}
p.old.tok = invalidTok
p.s.Init(qualType)
return p
}
func (p *parser) peek() token.Token {
if p.old.tok == invalidTok {
p.old.tok, p.old.lit = p.s.Scan()
}
return p.old.tok
}
func (p *parser) next() {
if p.old.tok != invalidTok { // support unget
p.tok, p.lit = p.old.tok, p.old.lit
p.old.tok = invalidTok
return
}
p.tok, p.lit = p.s.Scan()
}
func (p *parser) unget(tok token.Token, lit string) {
p.old.tok, p.old.lit = p.tok, p.lit
p.tok, p.lit = tok, lit
}
func (p *parser) skipUntil(tok token.Token) bool {
for {
p.next()
switch p.tok {
case tok:
return true
case token.EOF:
return false
}
}
}
func (p *parser) newErrorf(format string, args ...interface{}) *ParseTypeError {
return p.newError(fmt.Sprintf(format, args...))
}
func (p *parser) newError(errMsg string) *ParseTypeError {
return &ParseTypeError{QualType: p.s.Source(), ErrMsg: errMsg}
}
// TODO(xsw): check expect results
func (p *parser) expect(tokExp token.Token) error {
p.next()
if p.tok != tokExp {
return p.newErrorf("expect %v, got %v", tokExp, p.tok)
}
return nil
}
const (
flagShort = 1 << iota
flagLong
flagLongLong
flagUnsigned
flagSigned
flagComplex
flagStructOrUnion
)
func (p *parser) lookupType(tylit string, flags int) (t types.Type, err error) {
structOrUnion := (flags & flagStructOrUnion) != 0
_, o := gogen.LookupParent(p.scope, tylit, token.NoPos)
if o == nil {
return nil, &TypeNotFound{Literal: tylit, StructOrUnion: structOrUnion}
}
t = o.Type()
if !structOrUnion && flags != 0 {
tt, ok := t.(*types.Basic)
if !ok {
tyInt128 := p.conf.Int128()
if t == tyInt128 {
switch flags {
case flagSigned:
return tyInt128, nil
case flagUnsigned:
return p.conf.Uint128(), nil
}
}
} else if (flags & flagComplex) != 0 {
switch tt.Kind() {
case types.Float32:
return types.Typ[types.Complex64], nil
case types.Float64:
return types.Typ[types.Complex128], nil
case types.Int:
return types.Typ[types.Complex128], nil
}
} else {
switch tt.Kind() {
case types.Int:
if t = intTypes[flags&^flagSigned]; t != nil {
return
}
case types.Int8:
switch flags {
case flagUnsigned:
return types.Typ[types.Uint8], nil
case flagSigned:
return types.Typ[types.Int8], nil
}
case types.Float64:
switch flags {
case flagLong:
return ctypes.LongDouble, nil
}
}
}
log.Panicln("lookupType: TODO - invalid type")
return nil, ErrInvalidType
}
if t == types.Typ[types.Int] {
return ctypes.Int, nil
}
return
}
var intTypes = [...]types.Type{
0: ctypes.Int,
flagShort: types.Typ[types.Int16],
flagLong: ctypes.Long,
flagLong | flagLongLong: types.Typ[types.Int64],
flagUnsigned: ctypes.Uint,
flagShort | flagUnsigned: types.Typ[types.Uint16],
flagLong | flagUnsigned: ctypes.Ulong,
flagLong | flagLongLong | flagUnsigned: types.Typ[types.Uint64],
flagShort | flagLong | flagLongLong | flagUnsigned: nil,
}
func (p *parser) parseArray(t types.Type, inFlags int) (types.Type, error) {
var n int64
var err error
p.next()
switch p.tok {
case token.RBRACK: // ]
if (inFlags & FlagIsStructField) != 0 {
n = 0
} else {
n = -1
}
case token.INT:
if n, err = strconv.ParseInt(p.lit, 10, 64); err != nil {
return nil, p.newError(err.Error())
}
if err = p.expect(token.RBRACK); err != nil { // ]
return nil, err
}
default:
return nil, p.newError("array length not an integer")
}
if n >= 0 || (inFlags&(FlagIsExtern|FlagIsTypedef|FlagIsParam)) != 0 {
t = types.NewArray(t, n)
} else {
return nil, p.newError(emsgDefArrWithoutLen)
}
return t, nil
}
func (p *parser) parseArrays(t types.Type, inFlags int) (ret types.Type, err error) {
if t == nil {
return nil, p.newError("array to nil")
}
var tyArr types.Type
for {
if tyArr, err = p.parseArray(tyArr, inFlags); err != nil {
return
}
if p.peek() != token.LBRACK {
return newArraysEx(t, tyArr, inFlags), nil
}
p.next()
}
}
func (p *parser) parseFunc(pkg *types.Package, t types.Type, inFlags int) (ret types.Type, err error) {
var results *types.Tuple
if ctypes.NotVoid(t) {
results = types.NewTuple(types.NewParam(token.NoPos, pkg, "", t))
}
args, variadic, err := p.parseArgs(pkg)
if err != nil {
return
}
_ = inFlags
return ctypes.NewFunc(types.NewTuple(args...), results, variadic), nil
}
func (p *parser) parseArgs(pkg *types.Package) (args []*types.Var, variadic bool, err error) {
for {
arg, kind, e := p.parse(FlagIsParam)
if e != nil {
return nil, false, e
}
if ctypes.NotVoid(arg) {
args = append(args, types.NewParam(token.NoPos, pkg, "", arg))
}
if p.tok != token.COMMA {
variadic = (kind & KindFVariadic) != 0
break
}
}
if p.tok != token.RPAREN { // )
return nil, false, p.newError("expect )")
}
return
}
func (p *parser) parseStars() (nstar int) {
for isPtr(p.peek()) {
p.next()
nstar++
}
return
}
func (p *parser) parse(inFlags int) (t types.Type, kind int, err error) {
flags := 0
for {
p.next()
retry:
switch p.tok {
case token.IDENT:
ident:
switch lit := p.lit; lit {
case "unsigned":
flags |= flagUnsigned
case "short":
flags |= flagShort
case "long":
if (flags & flagLong) != 0 {
flags |= flagLongLong
} else {
flags |= flagLong
}
case "signed":
flags |= flagSigned
case "const":
kind |= KindFConst
case "volatile":
kind |= KindFVolatile
case "_Complex":
flags |= flagComplex
case "restrict", "_Nullable", "_Nonnull":
case "enum":
if err = p.expect(token.IDENT); err != nil {
return
}
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
t = ctypes.Int
continue
case "struct", "union":
p.next()
switch p.tok {
case token.IDENT:
case token.LPAREN:
if t == nil && p.conf.Anonym != nil {
p.skipUntil(token.RPAREN)
t = p.conf.Anonym
kind |= KindFAnonymous
continue
}
fallthrough
default:
log.Panicln("c.types.ParseType: struct/union - TODO:", p.lit)
}
lit = ctypes.MangledName(lit, p.lit)
flags |= flagStructOrUnion
fallthrough
default:
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
if t, err = p.lookupType(lit, flags); err != nil {
return
}
flags = 0
}
if flags != 0 {
p.next()
if p.tok == token.IDENT {
goto ident
}
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
if t, err = p.lookupType("int", flags); err != nil {
return
}
flags = 0
goto retry
}
case token.MUL: // *
if t == nil {
return nil, 0, p.newError("pointer to nil")
}
t = ctypes.NewPointer(t)
case token.LBRACK: // [
if t, err = p.parseArrays(t, inFlags); err != nil {
return
}
case token.LPAREN: // (
if t == nil {
log.Panicln("TODO")
return nil, 0, p.newError("no function return type")
}
var nstar = p.parseStars()
var nstarRet int
var tyArr types.Type
var pkg, isFn = p.conf.Pkg(), false
var args []*types.Var
var variadic bool
if nstar == 0 {
if getRetType(inFlags) {
err = nil
p.tok = token.EOF
return
}
if args, variadic, err = p.parseArgs(pkg); err != nil {
return
}
isFn = true
} else {
nextTok:
p.next()
switch p.tok {
case token.RPAREN: // )
case token.LPAREN: // (
if !isFn {
nstar, nstarRet = p.parseStars(), nstar
if nstar != 0 {
p.expect(token.RPAREN) // )
p.expect(token.LPAREN) // (
}
if args, variadic, err = p.parseArgs(pkg); err != nil {
return
}
isFn = true
goto nextTok
}
return nil, 0, p.newError("expect )")
case token.LBRACK:
if tyArr, err = p.parseArrays(ctypes.Void, 0); err != nil {
return
}
p.expect(token.RPAREN) // )
case token.IDENT:
switch p.lit {
case "_Nullable", "_Nonnull", "const", "volatile":
goto nextTok
}
fallthrough
default:
return nil, 0, p.newError("expect )")
}
}
p.next()
switch p.tok {
case token.LPAREN: // (
if t, err = p.parseFunc(pkg, t, inFlags); err != nil {
return
}
case token.LBRACK: // [
if t, err = p.parseArrays(t, 0); err != nil {
return
}
case token.EOF:
case token.IDENT:
if p.lit == "__attribute__" {
p.tok, p.lit = token.EOF, ""
p.unget(token.EOF, "")
break
}
fallthrough
default:
return nil, 0, p.newError("unexpected " + p.tok.String())
}
t = newPointers(t, nstarRet)
if isFn {
if getRetType(inFlags) {
p.tok = token.EOF
return
}
var results *types.Tuple
if ctypes.NotVoid(t) {
results = types.NewTuple(types.NewParam(token.NoPos, pkg, "", t))
}
t = ctypes.NewFunc(types.NewTuple(args...), results, variadic)
}
t = newPointers(t, nstar)
t = newArrays(t, tyArr)
case token.RPAREN:
if t == nil {
t = ctypes.Void
}
return
case token.COMMA, token.EOF:
if t == nil {
err = io.ErrUnexpectedEOF
}
return
case token.ELLIPSIS:
if t != nil {
return nil, 0, p.newError("illegal syntax: multiple types?")
}
t = ctypes.Valist
kind |= KindFVariadic
default:
log.Panicln("c.types.ParseType: unknown -", p.tok, p.lit)
}
}
}
func newPointers(t types.Type, nstar int) types.Type {
for nstar > 0 {
t = ctypes.NewPointer(t)
nstar--
}
return t
}
func isPtr(tok token.Token) bool {
return tok == token.MUL || tok == token.XOR // * or ^
}
func newArrays(t types.Type, tyArr types.Type) types.Type {
retry:
if arr, ok := tyArr.(*types.Array); ok {
t = types.NewArray(t, arr.Len())
tyArr = arr.Elem()
goto retry
}
return t
}
func newArraysEx(t types.Type, tyArr types.Type, inFlags int) types.Type {
t = newArrays(t, tyArr)
if arr, ok := t.(*types.Array); ok {
if (inFlags & FlagIsParam) != 0 {
t = ctypes.NewPointer(arr.Elem())
}
}
return t
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,303 @@
/*
* Copyright (c) 2022 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 scanner
import (
"fmt"
"go/token"
"unicode"
"unicode/utf8"
)
// An ErrorHandler may be provided to Scanner.Init. If a syntax error is
// encountered and a handler was installed, the handler is called with a
// position and an error message. The position points to the beginning of
// the offending token.
type ErrorHandler func(pos token.Position, msg string)
// A Scanner holds the scanner's internal state while processing
// a given text. It can be allocated as part of another data
// structure but must be initialized via Init before use.
type Scanner struct {
// immutable state
src string
// scanning state
ch rune // current character
offset int // character offset
rdOffset int // reading offset (position after current character)
// public state - ok to modify
ErrorCount int // number of errors encountered
OnErr func(msg string)
}
const (
bom = 0xFEFF // byte order mark, only permitted as very first character
eof = -1 // end of file
)
// Read the next Unicode char into s.ch.
// s.ch < 0 means end-of-file.
//
// For optimization, there is some overlap between this method and
// s.scanIdentifier.
func (s *Scanner) next() {
if s.rdOffset < len(s.src) {
s.offset = s.rdOffset
r, w := rune(s.src[s.rdOffset]), 1
switch {
case r == 0:
s.error("illegal character NUL")
case r >= utf8.RuneSelf:
// not ASCII
r, w = utf8.DecodeRuneInString(s.src[s.rdOffset:])
if r == utf8.RuneError && w == 1 {
s.error("illegal UTF-8 encoding")
} else if r == bom && s.offset > 0 {
s.error("illegal byte order mark")
}
}
s.rdOffset += w
s.ch = r
} else {
s.offset = len(s.src)
s.ch = eof
}
}
// peek returns the byte following the most recently read character without
// advancing the scanner. If the scanner is at EOF, peek returns 0.
func (s *Scanner) peek() byte {
if s.rdOffset < len(s.src) {
return s.src[s.rdOffset]
}
return 0
}
func (s *Scanner) Init(src string) {
s.src = src
s.ch = ' '
s.offset = 0
s.rdOffset = 0
s.ErrorCount = 0
s.next()
if s.ch == bom {
s.next() // ignore BOM at file beginning
}
}
func (s *Scanner) Source() string {
return s.src
}
func (s *Scanner) error(msg string) {
if s.OnErr != nil {
s.OnErr(msg)
}
s.ErrorCount++
}
func (s *Scanner) errorf(format string, args ...interface{}) {
s.error(fmt.Sprintf(format, args...))
}
func isLetter(ch rune) bool {
return 'a' <= lower(ch) && lower(ch) <= 'z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}
func isDigit(ch rune) bool {
return isDecimal(ch) || ch >= utf8.RuneSelf && unicode.IsDigit(ch)
}
// scanIdentifier reads the string of valid identifier characters at s.offset.
// It must only be called when s.ch is known to be a valid letter.
//
// Be careful when making changes to this function: it is optimized and affects
// scanning performance significantly.
func (s *Scanner) scanIdentifier() string {
offs := s.offset
// Optimize for the common case of an ASCII identifier.
//
// Ranging over s.src[s.rdOffset:] lets us avoid some bounds checks, and
// avoids conversions to runes.
//
// In case we encounter a non-ASCII character, fall back on the slower path
// of calling into s.next().
for rdOffset, b := range s.src[s.rdOffset:] {
if 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' || b == '_' || '0' <= b && b <= '9' {
// Avoid assigning a rune for the common case of an ascii character.
continue
}
s.rdOffset += rdOffset
if 0 < b && b < utf8.RuneSelf {
// Optimization: we've encountered an ASCII character that's not a letter
// or number. Avoid the call into s.next() and corresponding set up.
//
// Note that s.next() does some line accounting if s.ch is '\n', so this
// shortcut is only possible because we know that the preceding character
// is not '\n'.
s.ch = rune(b)
s.offset = s.rdOffset
s.rdOffset++
goto exit
}
// We know that the preceding character is valid for an identifier because
// scanIdentifier is only called when s.ch is a letter, so calling s.next()
// at s.rdOffset resets the scanner state.
s.next()
for isLetter(s.ch) || isDigit(s.ch) {
s.next()
}
goto exit
}
s.offset = len(s.src)
s.rdOffset = len(s.src)
s.ch = eof
exit:
return string(s.src[offs:s.offset])
}
func lower(ch rune) rune { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter
func isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' }
func isHex(ch rune) bool { return '0' <= ch && ch <= '9' || 'a' <= lower(ch) && lower(ch) <= 'f' }
func (s *Scanner) digits(base int, invalid *int) (digsep int) {
if base <= 10 {
max := rune('0' + base)
for isDecimal(s.ch) {
if s.ch >= max && *invalid < 0 {
*invalid = s.offset // record invalid rune offset
}
digsep = 1
s.next()
}
} else {
for isHex(s.ch) {
digsep = 1
s.next()
}
}
return
}
func (s *Scanner) scanNumber() (token.Token, string) {
offs := s.offset
base := 10 // number base
prefix := rune(0) // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b'
digsep := 0 // bit 0: digit present, bit 1: '_' present
invalid := -1 // index of invalid digit in literal, or < 0
if s.ch == '0' {
s.next()
switch lower(s.ch) {
case 'x':
s.next()
base, prefix = 16, 'x'
case 'o':
s.next()
base, prefix = 8, 'o'
case 'b':
s.next()
base, prefix = 2, 'b'
default:
base, prefix = 8, '0'
digsep = 1 // leading 0
}
}
digsep |= s.digits(base, &invalid)
if digsep&1 == 0 {
s.error(litname(prefix) + " has no digits")
}
lit := string(s.src[offs:s.offset])
if invalid >= 0 {
s.errorf("invalid digit %q in %s", lit[invalid-offs], litname(prefix))
}
return token.INT, lit
}
func litname(prefix rune) string {
switch prefix {
case 'x':
return "hexadecimal literal"
case 'o', '0':
return "octal literal"
case 'b':
return "binary literal"
}
return "decimal literal"
}
func (s *Scanner) skipWhitespace() {
for s.ch == ' ' || s.ch == '\t' || s.ch == '\n' || s.ch == '\r' {
s.next()
}
}
func (s *Scanner) Scan() (tok token.Token, lit string) {
s.skipWhitespace()
// determine token value
switch ch := s.ch; {
case isLetter(ch):
lit = s.scanIdentifier()
tok = token.IDENT
case isDecimal(ch):
tok, lit = s.scanNumber()
default:
s.next() // always make progress
switch ch {
case -1:
tok = token.EOF
case '.':
// fractions starting with a '.' are handled by outer switch
tok = token.PERIOD
if s.ch == '.' && s.peek() == '.' {
s.next()
s.next() // consume last '.'
tok = token.ELLIPSIS
}
case ',':
tok = token.COMMA
case '(':
tok = token.LPAREN
case ')':
tok = token.RPAREN
case '[':
tok = token.LBRACK
case ']':
tok = token.RBRACK
case '*':
tok = token.MUL
case '^':
tok = token.XOR
default:
// next reports unexpected BOMs - don't repeat
if ch != bom {
s.errorf("illegal character %#U", ch)
}
tok = token.ILLEGAL
lit = string(ch)
}
}
return
}

99
x/clang/types/types.go Normal file
View File

@@ -0,0 +1,99 @@
/*
* Copyright (c) 2022 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 types
import (
"go/token"
"go/types"
"unsafe"
"github.com/goplus/gogen"
)
// -----------------------------------------------------------------------------
var (
Void = types.Typ[types.UntypedNil]
UnsafePointer = types.Typ[types.UnsafePointer]
Int = types.Typ[types.Int32]
Uint = types.Typ[types.Uint32]
Long = types.Typ[uintptr(types.Int32)+unsafe.Sizeof(0)>>3] // int32/int64
Ulong = types.Typ[uintptr(types.Uint32)+unsafe.Sizeof(0)>>3] // uint32/uint64
NotImpl = UnsafePointer
LongDouble = types.Typ[types.Float64]
)
func NotVoid(t types.Type) bool {
return t != Void
}
func MangledName(tag, name string) string {
return tag + "_" + name // TODO: use sth to replace _
}
// -----------------------------------------------------------------------------
var (
ValistTag types.Type
Valist types.Type = types.NewSlice(gogen.TyEmptyInterface)
)
func init() {
vaTag := types.NewTypeName(token.NoPos, types.Unsafe, MangledName("struct", "__va_list_tag"), nil)
ValistTag = types.NewNamed(vaTag, types.NewStruct(nil, nil), nil)
types.Universe.Insert(vaTag)
}
// -----------------------------------------------------------------------------
func NewFunc(params, results *types.Tuple, variadic bool) *types.Signature {
return gogen.NewCSignature(params, results, variadic)
}
func NewPointer(typ types.Type) types.Type {
switch t := typ.(type) {
case *types.Basic:
if t == Void {
return types.Typ[types.UnsafePointer]
}
case *types.Signature:
if gogen.IsCSignature(t) {
return types.NewSignatureType(nil, nil, nil, t.Params(), t.Results(), t.Variadic())
}
case *types.Named:
if typ == ValistTag {
return Valist
}
}
return types.NewPointer(typ)
}
func IsFunc(typ types.Type) bool {
sig, ok := typ.(*types.Signature)
if ok {
ok = gogen.IsCSignature(sig)
}
return ok
}
func Identical(typ1, typ2 types.Type) bool {
return types.Identical(typ1, typ2)
}
// -----------------------------------------------------------------------------