Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
028c6cdf50 | ||
|
|
b08ae1dff3 | ||
|
|
37e7d66343 | ||
|
|
e6d06cc278 | ||
|
|
017fd150cd | ||
|
|
ea7e1de833 | ||
|
|
c2767be178 | ||
|
|
c60be43ac6 | ||
|
|
c373a5b505 | ||
|
|
da20aea408 | ||
|
|
973b5b90dc | ||
|
|
cffb5e9539 | ||
|
|
4b35586566 | ||
|
|
1e075d4830 | ||
|
|
c81b5b5df9 | ||
|
|
80b2b8d061 | ||
|
|
d1a7f63797 | ||
|
|
92359dd03b | ||
|
|
d1430c9f5a | ||
|
|
7feca4478e | ||
|
|
02b1d5ed84 | ||
|
|
99d74ce4b6 | ||
|
|
355094c7e2 | ||
|
|
7f61989869 | ||
|
|
7223ff004a | ||
|
|
df333fb144 | ||
|
|
5c08c55957 | ||
|
|
26b812a62a | ||
|
|
cd3d9c709f | ||
|
|
9da90e7ecf | ||
|
|
f17a4ca1de | ||
|
|
8ab662b373 | ||
|
|
fb839da81e | ||
|
|
244da4b10a | ||
|
|
c0c5271172 | ||
|
|
0066f8bd3f | ||
|
|
be9d209622 | ||
|
|
2bbd828f3a | ||
|
|
4f1b6e95a1 | ||
|
|
87ca3a39dc | ||
|
|
d7df46d578 | ||
|
|
489cbc4782 | ||
|
|
3c33a1d05e | ||
|
|
067cf0cba6 | ||
|
|
03a194a514 | ||
|
|
bdf45c0fcb | ||
|
|
6f679c05a3 | ||
|
|
0b131bd957 | ||
|
|
ebf4c80aff | ||
|
|
18a63e226a | ||
|
|
92f56a2f90 | ||
|
|
926e2d4a2e | ||
|
|
5d1d51dd58 | ||
|
|
52018cd424 | ||
|
|
e9153defee | ||
|
|
889fc8b6a9 | ||
|
|
9b9da3133d | ||
|
|
1c8edb0387 | ||
|
|
c0ef1598c9 | ||
|
|
bc1acee6f5 | ||
|
|
9f25d73826 | ||
|
|
f07a62d136 | ||
|
|
06d6b447e4 | ||
|
|
1cff02e4cc | ||
|
|
809a400f57 | ||
|
|
f1bb42f554 | ||
|
|
4fd8f84536 | ||
|
|
223c24450e | ||
|
|
8a7ddf4dc2 | ||
|
|
08217e5a5a | ||
|
|
424dbd9261 | ||
|
|
b615ada2c3 | ||
|
|
20a47873d0 | ||
|
|
d87ce1a124 | ||
|
|
91d012d33d | ||
|
|
330cb22351 | ||
|
|
236debab33 | ||
|
|
13a1c8ac4b | ||
|
|
29fad7b397 | ||
|
|
8eeac8a26d | ||
|
|
133d41d748 | ||
|
|
d444123062 | ||
|
|
4a5c8d3fbb | ||
|
|
afd3d40348 | ||
|
|
85da86a4f1 | ||
|
|
72d4f0f7f8 | ||
|
|
192b479f18 | ||
|
|
3e6dfa3c05 | ||
|
|
5bd28a1e9e | ||
|
|
a23a2601e4 | ||
|
|
3220b629c7 | ||
|
|
9cf122c31a | ||
|
|
75d513a78a | ||
|
|
3cbe4aac87 | ||
|
|
3e47a977e4 | ||
|
|
40855c2d2a | ||
|
|
b2319eda66 | ||
|
|
5c5b8e62e5 | ||
|
|
fbb1f89ab3 | ||
|
|
25b104cf13 | ||
|
|
be1599b418 | ||
|
|
d462e548b1 | ||
|
|
df1e4708f5 | ||
|
|
cf02f4a34f | ||
|
|
480cf09177 | ||
|
|
52a64a7770 | ||
|
|
8d3cb246c2 | ||
|
|
8f15fd45f2 | ||
|
|
afd02b3d78 |
38
README.md
38
README.md
@@ -9,3 +9,41 @@ llgo - A Go compiler based on LLVM
|
|||||||
[](https://github.com/goplus/gop)
|
[](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
21
_demo/concat/concat.go
Normal 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
63
_demo/genints/gen_ints.go
Normal 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
13
_demo/hello/hello.go
Normal 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
25
_demo/qsort/qsort.go
Normal 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
76
c/c.go
Normal 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)
|
||||||
71
chore/clangast/clangast.go
Normal file
71
chore/clangast/clangast.go
Normal 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
41
chore/clangpp/clangpp.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
174
cl/_testdata/print/in.go
Normal 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
213
cl/_testdata/print/out.ll
Normal 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)
|
||||||
@@ -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, ...)
|
||||||
|
|||||||
@@ -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, ...)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
source_filename = "main"
|
source_filename = "main"
|
||||||
|
|
||||||
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
||||||
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
|
||||||
|
|
||||||
@"main.init$guard" = global ptr null
|
@"main.init$guard" = global ptr null
|
||||||
@0 = private unnamed_addr constant [13 x i8] c"Hello world\0A\00", align 1
|
@0 = private unnamed_addr constant [13 x i8] c"Hello world\0A\00", align 1
|
||||||
@@ -43,7 +42,7 @@ declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/ll
|
|||||||
|
|
||||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||||
|
|
||||||
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
|
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
|
||||||
|
|
||||||
declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String")
|
declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String")
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,51 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/goplus/llgo/internal/runtime/c"
|
||||||
|
)
|
||||||
|
|
||||||
var a int64 = 1<<63 - 1
|
var a int64 = 1<<63 - 1
|
||||||
var b int64 = -1 << 63
|
var b int64 = -1 << 63
|
||||||
var c uint64 = 1<<64 - 1
|
var n uint64 = 1<<64 - 1
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var a = []int{1, 2, 3, 4}
|
var s = []int{1, 2, 3, 4}
|
||||||
_ = len(a)
|
var a = [...]int{1, 2, 3, 4}
|
||||||
_ = len([]int{1, 2, 3, 4})
|
|
||||||
|
out(len(s))
|
||||||
|
out(len([]int{1, 2, 3, 4}))
|
||||||
|
out(len(a))
|
||||||
|
out(len(&a))
|
||||||
|
out(len([4]int{1, 2, 3, 4}))
|
||||||
|
|
||||||
|
out(cap(s))
|
||||||
|
out(cap(a))
|
||||||
|
out(cap(&a))
|
||||||
|
|
||||||
|
out(len(s[1:]))
|
||||||
|
out(cap(s[1:]))
|
||||||
|
out(len(s[1:2]))
|
||||||
|
out(cap(s[1:2]))
|
||||||
|
out(len(s[1:2:2]))
|
||||||
|
out(cap(s[1:2:2]))
|
||||||
|
|
||||||
|
out(len(a[1:]))
|
||||||
|
out(cap(a[1:]))
|
||||||
|
out(len(a[1:2]))
|
||||||
|
out(cap(a[1:2]))
|
||||||
|
out(len(a[1:2:2]))
|
||||||
|
out(cap(a[1:2:2]))
|
||||||
|
|
||||||
|
string_len("hello")
|
||||||
|
string_len("hello"[1:])
|
||||||
|
string_len("hello"[1:2])
|
||||||
|
string_len("hello"[5:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func string_len(s string) {
|
||||||
|
out(len(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func out(n int) {
|
||||||
|
c.Printf(c.Str("%d\n"), n)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,17 @@
|
|||||||
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 }
|
||||||
|
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
||||||
|
|
||||||
@main.a = global ptr null
|
@main.a = global ptr null
|
||||||
@main.b = global ptr null
|
@main.b = global ptr null
|
||||||
@main.c = global ptr null
|
|
||||||
@"main.init$guard" = global ptr null
|
@"main.init$guard" = global ptr null
|
||||||
|
@main.n = global ptr null
|
||||||
|
@0 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
|
||||||
|
@1 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
|
||||||
|
@2 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
|
||||||
|
@3 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
|
||||||
|
@4 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
|
||||||
|
|
||||||
define void @main.init() {
|
define void @main.init() {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
@@ -17,7 +23,7 @@ _llgo_1: ; preds = %_llgo_0
|
|||||||
store i1 true, ptr @"main.init$guard", align 1
|
store i1 true, ptr @"main.init$guard", align 1
|
||||||
store i64 9223372036854775807, ptr @main.a, align 4
|
store i64 9223372036854775807, ptr @main.a, align 4
|
||||||
store i64 -9223372036854775808, ptr @main.b, align 4
|
store i64 -9223372036854775808, ptr @main.b, align 4
|
||||||
store i64 -1, ptr @main.c, align 4
|
store i64 -1, ptr @main.n, align 4
|
||||||
br label %_llgo_2
|
br label %_llgo_2
|
||||||
|
|
||||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||||
@@ -28,7 +34,7 @@ define void @main() {
|
|||||||
_llgo_0:
|
_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.Alloc"(i64 32)
|
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||||
%1 = getelementptr inbounds i64, ptr %0, i64 0
|
%1 = getelementptr inbounds i64, ptr %0, i64 0
|
||||||
store i64 1, ptr %1, align 4
|
store i64 1, ptr %1, align 4
|
||||||
%2 = getelementptr inbounds i64, ptr %0, i64 1
|
%2 = getelementptr inbounds i64, ptr %0, i64 1
|
||||||
@@ -37,26 +43,132 @@ _llgo_0:
|
|||||||
store i64 3, ptr %3, align 4
|
store i64 3, ptr %3, align 4
|
||||||
%4 = getelementptr inbounds i64, ptr %0, i64 3
|
%4 = getelementptr inbounds i64, ptr %0, i64 3
|
||||||
store i64 4, ptr %4, align 4
|
store i64 4, ptr %4, align 4
|
||||||
%5 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 4, i64 4)
|
%5 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %0, i64 8, i64 4, i64 0, i64 4, i64 4)
|
||||||
%6 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
%6 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||||
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
%7 = getelementptr inbounds i64, ptr %6, i64 0
|
||||||
%8 = getelementptr inbounds i64, ptr %7, i64 0
|
%8 = getelementptr inbounds i64, ptr %6, i64 1
|
||||||
store i64 1, ptr %8, align 4
|
%9 = getelementptr inbounds i64, ptr %6, i64 2
|
||||||
%9 = getelementptr inbounds i64, ptr %7, i64 1
|
%10 = getelementptr inbounds i64, ptr %6, i64 3
|
||||||
store i64 2, ptr %9, align 4
|
store i64 1, ptr %7, align 4
|
||||||
%10 = getelementptr inbounds i64, ptr %7, i64 2
|
store i64 2, ptr %8, align 4
|
||||||
store i64 3, ptr %10, align 4
|
store i64 3, ptr %9, align 4
|
||||||
%11 = getelementptr inbounds i64, ptr %7, i64 3
|
store i64 4, ptr %10, align 4
|
||||||
store i64 4, ptr %11, align 4
|
%11 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
%12 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %7, i64 4, i64 4)
|
call void @main.out(i64 %11)
|
||||||
%13 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %12)
|
%12 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||||
|
%13 = getelementptr inbounds i64, ptr %12, i64 0
|
||||||
|
store i64 1, ptr %13, align 4
|
||||||
|
%14 = getelementptr inbounds i64, ptr %12, i64 1
|
||||||
|
store i64 2, ptr %14, align 4
|
||||||
|
%15 = getelementptr inbounds i64, ptr %12, i64 2
|
||||||
|
store i64 3, ptr %15, align 4
|
||||||
|
%16 = getelementptr inbounds i64, ptr %12, i64 3
|
||||||
|
store i64 4, ptr %16, align 4
|
||||||
|
%17 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %12, i64 8, i64 4, i64 0, i64 4, i64 4)
|
||||||
|
%18 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %17)
|
||||||
|
call void @main.out(i64 %18)
|
||||||
|
call void @main.out(i64 4)
|
||||||
|
call void @main.out(i64 4)
|
||||||
|
call void @main.out(i64 4)
|
||||||
|
%19 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
call void @main.out(i64 %19)
|
||||||
|
call void @main.out(i64 4)
|
||||||
|
call void @main.out(i64 4)
|
||||||
|
%20 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%21 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%22 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%23 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %22, i64 8, i64 %20, i64 1, i64 %21, i64 %20)
|
||||||
|
%24 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %23)
|
||||||
|
call void @main.out(i64 %24)
|
||||||
|
%25 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%26 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%27 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%28 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %27, i64 8, i64 %25, i64 1, i64 %26, i64 %25)
|
||||||
|
%29 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %28)
|
||||||
|
call void @main.out(i64 %29)
|
||||||
|
%30 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%31 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%32 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %31, i64 8, i64 %30, i64 1, i64 2, i64 %30)
|
||||||
|
%33 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %32)
|
||||||
|
call void @main.out(i64 %33)
|
||||||
|
%34 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%35 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%36 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %35, i64 8, i64 %34, i64 1, i64 2, i64 %34)
|
||||||
|
%37 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %36)
|
||||||
|
call void @main.out(i64 %37)
|
||||||
|
%38 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%39 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%40 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %39, i64 8, i64 %38, i64 1, i64 2, i64 2)
|
||||||
|
%41 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %40)
|
||||||
|
call void @main.out(i64 %41)
|
||||||
|
%42 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%43 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
|
%44 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %43, i64 8, i64 %42, i64 1, i64 2, i64 2)
|
||||||
|
%45 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %44)
|
||||||
|
call void @main.out(i64 %45)
|
||||||
|
%46 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 4, i64 4)
|
||||||
|
%47 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %46)
|
||||||
|
call void @main.out(i64 %47)
|
||||||
|
%48 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 4, i64 4)
|
||||||
|
%49 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %48)
|
||||||
|
call void @main.out(i64 %49)
|
||||||
|
%50 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 2, i64 4)
|
||||||
|
%51 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %50)
|
||||||
|
call void @main.out(i64 %51)
|
||||||
|
%52 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 2, i64 4)
|
||||||
|
%53 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %52)
|
||||||
|
call void @main.out(i64 %53)
|
||||||
|
%54 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 2, i64 2)
|
||||||
|
%55 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %54)
|
||||||
|
call void @main.out(i64 %55)
|
||||||
|
%56 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 2, i64 2)
|
||||||
|
%57 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %56)
|
||||||
|
call void @main.out(i64 %57)
|
||||||
|
%58 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 5)
|
||||||
|
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %58)
|
||||||
|
%59 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 5)
|
||||||
|
%60 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %59)
|
||||||
|
%61 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %59, i64 1, i64 %60)
|
||||||
|
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %61)
|
||||||
|
%62 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 5)
|
||||||
|
%63 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %62, i64 1, i64 2)
|
||||||
|
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %63)
|
||||||
|
%64 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 5)
|
||||||
|
%65 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %64)
|
||||||
|
%66 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %64, i64 5, i64 %65)
|
||||||
|
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %66)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @main.out(i64 %0) {
|
||||||
|
_llgo_0:
|
||||||
|
%1 = call i32 (ptr, ...) @printf(ptr @4, i64 %0)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
||||||
|
_llgo_0:
|
||||||
|
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %0)
|
||||||
|
call void @main.out(i64 %1)
|
||||||
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.Alloc"(i64)
|
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||||
|
|
||||||
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr, i64, i64)
|
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
|
||||||
|
|
||||||
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
|
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
|
||||||
|
|
||||||
|
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice")
|
||||||
|
|
||||||
|
declare ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice")
|
||||||
|
|
||||||
|
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
|
||||||
|
|
||||||
|
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
|
||||||
|
|
||||||
|
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String", i64, i64)
|
||||||
|
|
||||||
|
declare i32 @printf(ptr, ...)
|
||||||
|
|||||||
18
cl/_testrt/callback/in.go
Normal file
18
cl/_testrt/callback/in.go
Normal 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)
|
||||||
|
}
|
||||||
64
cl/_testrt/callback/out.ll
Normal file
64
cl/_testrt/callback/out.ll
Normal 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
18
cl/_testrt/closure/in.go
Normal 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
84
cl/_testrt/closure/out.ll
Normal 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, ...)
|
||||||
17
cl/_testrt/concat/in.go
Normal file
17
cl/_testrt/concat/in.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/goplus/llgo/internal/runtime/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))
|
||||||
|
}
|
||||||
106
cl/_testrt/concat/out.ll
Normal file
106
cl/_testrt/concat/out.ll
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
; ModuleID = 'main'
|
||||||
|
source_filename = "main"
|
||||||
|
|
||||||
|
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
||||||
|
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||||
|
|
||||||
|
@"main.init$guard" = global ptr null
|
||||||
|
@0 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||||
|
@1 = private unnamed_addr constant [6 x i8] c"Hello\00", align 1
|
||||||
|
@2 = private unnamed_addr constant [2 x i8] c" \00", align 1
|
||||||
|
@3 = private unnamed_addr constant [6 x i8] c"World\00", align 1
|
||||||
|
@__stderrp = external global ptr
|
||||||
|
@4 = private unnamed_addr constant [8 x i8] c"Hi, %s\0A\00", align 1
|
||||||
|
|
||||||
|
define %"github.com/goplus/llgo/internal/runtime.String" @main.concat(%"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 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 0)
|
||||||
|
%3 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %2, 0
|
||||||
|
%4 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %2, 1
|
||||||
|
br label %_llgo_1
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_2, %_llgo_0
|
||||||
|
%5 = phi ptr [ %3, %_llgo_0 ], [ %18, %_llgo_2 ]
|
||||||
|
%6 = phi i64 [ %4, %_llgo_0 ], [ %19, %_llgo_2 ]
|
||||||
|
%7 = phi i64 [ -1, %_llgo_0 ], [ %12, %_llgo_2 ]
|
||||||
|
%8 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
|
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %8, i32 0, i32 0
|
||||||
|
store ptr %5, ptr %9, align 8
|
||||||
|
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %8, i32 0, i32 1
|
||||||
|
store i64 %6, ptr %10, align 4
|
||||||
|
%11 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %8, align 8
|
||||||
|
%12 = add i64 %7, 1
|
||||||
|
%13 = icmp slt i64 %12, %1
|
||||||
|
br i1 %13, label %_llgo_2, label %_llgo_3
|
||||||
|
|
||||||
|
_llgo_2: ; preds = %_llgo_1
|
||||||
|
%14 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
|
||||||
|
%15 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %14, i64 %12
|
||||||
|
%16 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %15, align 8
|
||||||
|
%17 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String" %11, %"github.com/goplus/llgo/internal/runtime.String" %16)
|
||||||
|
%18 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %17, 0
|
||||||
|
%19 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %17, 1
|
||||||
|
br label %_llgo_1
|
||||||
|
|
||||||
|
_llgo_3: ; preds = %_llgo_1
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.String" %11
|
||||||
|
}
|
||||||
|
|
||||||
|
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 @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 48)
|
||||||
|
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i64 0
|
||||||
|
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 5)
|
||||||
|
store %"github.com/goplus/llgo/internal/runtime.String" %2, ptr %1, align 8
|
||||||
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i64 1
|
||||||
|
%4 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 1)
|
||||||
|
store %"github.com/goplus/llgo/internal/runtime.String" %4, ptr %3, align 8
|
||||||
|
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i64 2
|
||||||
|
%6 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 5)
|
||||||
|
store %"github.com/goplus/llgo/internal/runtime.String" %6, ptr %5, align 8
|
||||||
|
%7 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %0, i64 16, i64 3, i64 0, i64 3, i64 3)
|
||||||
|
%8 = call %"github.com/goplus/llgo/internal/runtime.String" @main.concat(%"github.com/goplus/llgo/internal/runtime.Slice" %7)
|
||||||
|
%9 = load ptr, ptr @__stderrp, align 8
|
||||||
|
%10 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %8)
|
||||||
|
%11 = add i64 %10, 1
|
||||||
|
%12 = alloca i8, i64 %11, align 1
|
||||||
|
%13 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %12, %"github.com/goplus/llgo/internal/runtime.String" %8)
|
||||||
|
%14 = call i32 (ptr, ptr, ...) @fprintf(ptr %9, ptr @4, ptr %13)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
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 %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String", %"github.com/goplus/llgo/internal/runtime.String")
|
||||||
|
|
||||||
|
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
|
||||||
|
|
||||||
|
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||||
|
|
||||||
|
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||||
|
|
||||||
|
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
|
||||||
|
|
||||||
|
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
|
||||||
|
|
||||||
|
declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String")
|
||||||
|
|
||||||
|
declare i32 @fprintf(ptr, ptr, ...)
|
||||||
@@ -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, ...)
|
||||||
|
|||||||
19
cl/_testrt/cvar/in.go
Normal file
19
cl/_testrt/cvar/in.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import _ "unsafe"
|
||||||
|
|
||||||
|
//go:linkname barX _bar_x
|
||||||
|
var barX struct {
|
||||||
|
Arr [16]int8
|
||||||
|
Callbacks [2]func()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname barY _bar_y
|
||||||
|
var barY struct {
|
||||||
|
Arr [16]int8
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
_ = barX
|
||||||
|
_ = barY
|
||||||
|
}
|
||||||
30
cl/_testrt/cvar/out.ll
Normal file
30
cl/_testrt/cvar/out.ll
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
; ModuleID = 'main'
|
||||||
|
source_filename = "main"
|
||||||
|
|
||||||
|
@_bar_x = external global ptr
|
||||||
|
@_bar_y = external global ptr
|
||||||
|
@"main.init$guard" = global ptr null
|
||||||
|
|
||||||
|
define void @main.init() {
|
||||||
|
_llgo_0:
|
||||||
|
%0 = load i1, ptr @"main.init$guard", align 1
|
||||||
|
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_0
|
||||||
|
store i1 true, ptr @"main.init$guard", align 1
|
||||||
|
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 = load { [16 x i8], [2 x ptr] }, ptr @_bar_x, align 8
|
||||||
|
%1 = load { [16 x i8] }, ptr @_bar_y, align 1
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||||
16
cl/_testrt/fprintf/in.go
Normal file
16
cl/_testrt/fprintf/in.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
//go:linkname cstr llgo.cstr
|
||||||
|
func cstr(string) *int8
|
||||||
|
|
||||||
|
//go:linkname stderr __stderrp
|
||||||
|
var stderr unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname fprintf C.fprintf
|
||||||
|
func fprintf(fp unsafe.Pointer, format *int8, __llgo_va_list ...any)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fprintf(stderr, cstr("Hello %d\n"), 100)
|
||||||
|
}
|
||||||
32
cl/_testrt/fprintf/out.ll
Normal file
32
cl/_testrt/fprintf/out.ll
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
; ModuleID = 'main'
|
||||||
|
source_filename = "main"
|
||||||
|
|
||||||
|
@"main.init$guard" = global ptr null
|
||||||
|
@__stderrp = external global ptr
|
||||||
|
@0 = private unnamed_addr constant [10 x i8] c"Hello %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()
|
||||||
|
%0 = load ptr, ptr @__stderrp, align 8
|
||||||
|
call void (ptr, ptr, ...) @fprintf(ptr %0, ptr @0, i64 100)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||||
|
|
||||||
|
declare void @fprintf(ptr, ptr, ...)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
; ModuleID = 'main'
|
; ModuleID = 'main'
|
||||||
source_filename = "main"
|
source_filename = "main"
|
||||||
|
|
||||||
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
|
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, { ptr, ptr }, ptr, i32, i32 }
|
||||||
|
|
||||||
@main.basicTypes = global ptr null
|
@main.basicTypes = global ptr null
|
||||||
@"main.init$guard" = global ptr null
|
@"main.init$guard" = global ptr null
|
||||||
@@ -17,7 +17,7 @@ _llgo_0:
|
|||||||
|
|
||||||
define ptr @main.basicType(i64 %0) {
|
define ptr @main.basicType(i64 %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 48)
|
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 56)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 0
|
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 0
|
||||||
%3 = getelementptr inbounds i64, ptr @main.sizeBasicTypes, i64 %0
|
%3 = getelementptr inbounds i64, ptr @main.sizeBasicTypes, i64 %0
|
||||||
%4 = load i64, ptr %3, align 4
|
%4 = load i64, ptr %3, align 4
|
||||||
@@ -62,7 +62,7 @@ _llgo_0:
|
|||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64)
|
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||||
|
|
||||||
declare void @"github.com/goplus/llgo/internal/abi.init"()
|
declare void @"github.com/goplus/llgo/internal/abi.init"()
|
||||||
|
|
||||||
|
|||||||
17
cl/_testrt/gotypes/in.go
Normal file
17
cl/_testrt/gotypes/in.go
Normal 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
34
cl/_testrt/gotypes/out.ll
Normal 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"()
|
||||||
25
cl/_testrt/index/in.go
Normal file
25
cl/_testrt/index/in.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/goplus/llgo/internal/runtime/c"
|
||||||
|
|
||||||
|
type point struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := [...]point{{1, 2}, {3, 4}, {5, 6}}[2]
|
||||||
|
c.Printf(c.Str("%d %d\n"), a.x, a.y)
|
||||||
|
|
||||||
|
b := [...][2]int{[2]int{1, 2}, [2]int{3, 4}}[1]
|
||||||
|
c.Printf(c.Str("%d %d\n"), b[0], b[1])
|
||||||
|
|
||||||
|
var i int = 2
|
||||||
|
n := [...]int{1, 2, 3, 4, 5}[i]
|
||||||
|
c.Printf(c.Str("%d\n"), n)
|
||||||
|
c.Printf(c.Str("%d\n"), [...]int{1, 2, 3, 4, 5}[i])
|
||||||
|
|
||||||
|
s := "123456"
|
||||||
|
c.Printf(c.Str("%c\n"), s[i])
|
||||||
|
c.Printf(c.Str("%c\n"), "123456"[1])
|
||||||
|
}
|
||||||
138
cl/_testrt/index/out.ll
Normal file
138
cl/_testrt/index/out.ll
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
; ModuleID = 'main'
|
||||||
|
source_filename = "main"
|
||||||
|
|
||||||
|
%main.point = type { i64, i64 }
|
||||||
|
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
||||||
|
|
||||||
|
@"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
|
||||||
|
@2 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
|
||||||
|
@3 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
|
||||||
|
@4 = private unnamed_addr constant [4 x i8] c"%c\0A\00", align 1
|
||||||
|
@5 = private unnamed_addr constant [7 x i8] c"123456\00", align 1
|
||||||
|
@6 = private unnamed_addr constant [4 x i8] c"%c\0A\00", align 1
|
||||||
|
@7 = private unnamed_addr constant [7 x i8] c"123456\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()
|
||||||
|
%0 = alloca %main.point, align 8
|
||||||
|
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 16)
|
||||||
|
%2 = alloca [3 x %main.point], align 8
|
||||||
|
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 48)
|
||||||
|
%4 = getelementptr inbounds %main.point, ptr %3, i64 0
|
||||||
|
%5 = getelementptr inbounds %main.point, ptr %4, i32 0, i32 0
|
||||||
|
%6 = getelementptr inbounds %main.point, ptr %4, i32 0, i32 1
|
||||||
|
%7 = getelementptr inbounds %main.point, ptr %3, i64 1
|
||||||
|
%8 = getelementptr inbounds %main.point, ptr %7, i32 0, i32 0
|
||||||
|
%9 = getelementptr inbounds %main.point, ptr %7, i32 0, i32 1
|
||||||
|
%10 = getelementptr inbounds %main.point, ptr %3, i64 2
|
||||||
|
%11 = getelementptr inbounds %main.point, ptr %10, i32 0, i32 0
|
||||||
|
%12 = getelementptr inbounds %main.point, ptr %10, i32 0, i32 1
|
||||||
|
store i64 1, ptr %5, align 4
|
||||||
|
store i64 2, ptr %6, align 4
|
||||||
|
store i64 3, ptr %8, align 4
|
||||||
|
store i64 4, ptr %9, align 4
|
||||||
|
store i64 5, ptr %11, align 4
|
||||||
|
store i64 6, ptr %12, align 4
|
||||||
|
%13 = load [3 x %main.point], ptr %3, align 4
|
||||||
|
%14 = getelementptr inbounds %main.point, ptr %3, i64 2
|
||||||
|
%15 = load %main.point, ptr %14, align 4
|
||||||
|
store %main.point %15, ptr %1, align 4
|
||||||
|
%16 = getelementptr inbounds %main.point, ptr %1, i32 0, i32 0
|
||||||
|
%17 = load i64, ptr %16, align 4
|
||||||
|
%18 = getelementptr inbounds %main.point, ptr %1, i32 0, i32 1
|
||||||
|
%19 = load i64, ptr %18, align 4
|
||||||
|
%20 = call i32 (ptr, ...) @printf(ptr @0, i64 %17, i64 %19)
|
||||||
|
%21 = alloca [2 x i64], align 8
|
||||||
|
%22 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %21, i64 16)
|
||||||
|
%23 = alloca [2 x [2 x i64]], align 8
|
||||||
|
%24 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %23, i64 32)
|
||||||
|
%25 = getelementptr inbounds [2 x i64], ptr %24, i64 0
|
||||||
|
%26 = getelementptr inbounds i64, ptr %25, i64 0
|
||||||
|
%27 = getelementptr inbounds i64, ptr %25, i64 1
|
||||||
|
%28 = getelementptr inbounds [2 x i64], ptr %24, i64 1
|
||||||
|
%29 = getelementptr inbounds i64, ptr %28, i64 0
|
||||||
|
%30 = getelementptr inbounds i64, ptr %28, i64 1
|
||||||
|
store i64 1, ptr %26, align 4
|
||||||
|
store i64 2, ptr %27, align 4
|
||||||
|
store i64 3, ptr %29, align 4
|
||||||
|
store i64 4, ptr %30, align 4
|
||||||
|
%31 = load [2 x [2 x i64]], ptr %24, align 4
|
||||||
|
%32 = getelementptr inbounds [2 x i64], ptr %24, i64 1
|
||||||
|
%33 = load [2 x i64], ptr %32, align 4
|
||||||
|
store [2 x i64] %33, ptr %22, align 4
|
||||||
|
%34 = getelementptr inbounds i64, ptr %22, i64 0
|
||||||
|
%35 = load i64, ptr %34, align 4
|
||||||
|
%36 = getelementptr inbounds i64, ptr %22, i64 1
|
||||||
|
%37 = load i64, ptr %36, align 4
|
||||||
|
%38 = call i32 (ptr, ...) @printf(ptr @1, i64 %35, i64 %37)
|
||||||
|
%39 = alloca [5 x i64], align 8
|
||||||
|
%40 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %39, i64 40)
|
||||||
|
%41 = getelementptr inbounds i64, ptr %40, i64 0
|
||||||
|
%42 = getelementptr inbounds i64, ptr %40, i64 1
|
||||||
|
%43 = getelementptr inbounds i64, ptr %40, i64 2
|
||||||
|
%44 = getelementptr inbounds i64, ptr %40, i64 3
|
||||||
|
%45 = getelementptr inbounds i64, ptr %40, i64 4
|
||||||
|
store i64 1, ptr %41, align 4
|
||||||
|
store i64 2, ptr %42, align 4
|
||||||
|
store i64 3, ptr %43, align 4
|
||||||
|
store i64 4, ptr %44, align 4
|
||||||
|
store i64 5, ptr %45, align 4
|
||||||
|
%46 = load [5 x i64], ptr %40, align 4
|
||||||
|
%47 = getelementptr inbounds i64, ptr %40, i64 2
|
||||||
|
%48 = load i64, ptr %47, align 4
|
||||||
|
%49 = call i32 (ptr, ...) @printf(ptr @2, i64 %48)
|
||||||
|
%50 = alloca [5 x i64], align 8
|
||||||
|
%51 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %50, i64 40)
|
||||||
|
%52 = getelementptr inbounds i64, ptr %51, i64 0
|
||||||
|
%53 = getelementptr inbounds i64, ptr %51, i64 1
|
||||||
|
%54 = getelementptr inbounds i64, ptr %51, i64 2
|
||||||
|
%55 = getelementptr inbounds i64, ptr %51, i64 3
|
||||||
|
%56 = getelementptr inbounds i64, ptr %51, i64 4
|
||||||
|
store i64 1, ptr %52, align 4
|
||||||
|
store i64 2, ptr %53, align 4
|
||||||
|
store i64 3, ptr %54, align 4
|
||||||
|
store i64 4, ptr %55, align 4
|
||||||
|
store i64 5, ptr %56, align 4
|
||||||
|
%57 = load [5 x i64], ptr %51, align 4
|
||||||
|
%58 = getelementptr inbounds i64, ptr %51, i64 2
|
||||||
|
%59 = load i64, ptr %58, align 4
|
||||||
|
%60 = call i32 (ptr, ...) @printf(ptr @3, i64 %59)
|
||||||
|
%61 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @5, i64 6)
|
||||||
|
%62 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %61)
|
||||||
|
%63 = getelementptr inbounds i8, ptr %62, i64 2
|
||||||
|
%64 = load i8, ptr %63, align 1
|
||||||
|
%65 = call i32 (ptr, ...) @printf(ptr @4, i8 %64)
|
||||||
|
%66 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @7, i64 6)
|
||||||
|
%67 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %66)
|
||||||
|
%68 = getelementptr inbounds i8, ptr %67, i64 1
|
||||||
|
%69 = load i8, ptr %68, align 1
|
||||||
|
%70 = call i32 (ptr, ...) @printf(ptr @6, i8 %69)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||||
|
|
||||||
|
declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64)
|
||||||
|
|
||||||
|
declare i32 @printf(ptr, ...)
|
||||||
|
|
||||||
|
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")
|
||||||
42
cl/_testrt/intgen/in.go
Normal file
42
cl/_testrt/intgen/in.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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 {
|
||||||
|
a := make([]c.Int, n)
|
||||||
|
for i := range a {
|
||||||
|
a[i] = gen()
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
for _, v := range genInts(5, c.Rand) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
195
cl/_testrt/intgen/out.ll
Normal file
195
cl/_testrt/intgen/out.ll
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
; ModuleID = 'main'
|
||||||
|
source_filename = "main"
|
||||||
|
|
||||||
|
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||||
|
%main.generator = type { i32 }
|
||||||
|
|
||||||
|
@"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 [4 x i8] c"%d\0A\00", align 1
|
||||||
|
|
||||||
|
define %"github.com/goplus/llgo/internal/runtime.Slice" @main.genInts(i64 %0, { ptr, ptr } %1) {
|
||||||
|
_llgo_0:
|
||||||
|
%2 = mul i64 %0, 4
|
||||||
|
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 %2)
|
||||||
|
%4 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %3, i64 %0, i64 %0)
|
||||||
|
%5 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %4)
|
||||||
|
br label %_llgo_1
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_2, %_llgo_0
|
||||||
|
%6 = phi i64 [ -1, %_llgo_0 ], [ %7, %_llgo_2 ]
|
||||||
|
%7 = add i64 %6, 1
|
||||||
|
%8 = icmp slt i64 %7, %5
|
||||||
|
br i1 %8, label %_llgo_2, label %_llgo_3
|
||||||
|
|
||||||
|
_llgo_2: ; preds = %_llgo_1
|
||||||
|
%9 = extractvalue { ptr, ptr } %1, 1
|
||||||
|
%10 = extractvalue { ptr, ptr } %1, 0
|
||||||
|
%11 = call i32 %10(ptr %9)
|
||||||
|
%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
|
||||||
|
|
||||||
|
_llgo_3: ; preds = %_llgo_1
|
||||||
|
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() {
|
||||||
|
_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.rand, 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
|
||||||
|
%4 = call %"github.com/goplus/llgo/internal/runtime.Slice" @main.genInts(i64 5, { ptr, ptr } %3)
|
||||||
|
%5 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %4)
|
||||||
|
br label %_llgo_1
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_2, %_llgo_0
|
||||||
|
%6 = phi i64 [ -1, %_llgo_0 ], [ %7, %_llgo_2 ]
|
||||||
|
%7 = add i64 %6, 1
|
||||||
|
%8 = icmp slt i64 %7, %5
|
||||||
|
br i1 %8, label %_llgo_2, label %_llgo_3
|
||||||
|
|
||||||
|
_llgo_2: ; preds = %_llgo_1
|
||||||
|
%9 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %4)
|
||||||
|
%10 = getelementptr inbounds i32, ptr %9, i64 %7
|
||||||
|
%11 = load i32, ptr %10, align 4
|
||||||
|
%12 = call i32 (ptr, ...) @printf(ptr @0, i32 %11)
|
||||||
|
br label %_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
|
||||||
|
}
|
||||||
|
|
||||||
|
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||||
|
|
||||||
|
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr, i64, 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 void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||||
|
|
||||||
|
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, ...)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
15
cl/_testrt/linkname/in.go
Normal file
15
cl/_testrt/linkname/in.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/internal/runtime/c"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname print github.com/goplus/llgo/cl/internal/linktarget.F
|
||||||
|
func print(a, b, c, d *c.Char)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
print(c.Str("a"), c.Str("b"), c.Str("c"), c.Str("d"))
|
||||||
|
print(c.Str("1"), c.Str("2"), c.Str("3"), c.Str("4"))
|
||||||
|
}
|
||||||
38
cl/_testrt/linkname/out.ll
Normal file
38
cl/_testrt/linkname/out.ll
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
; ModuleID = 'main'
|
||||||
|
source_filename = "main"
|
||||||
|
|
||||||
|
@"main.init$guard" = global ptr null
|
||||||
|
@0 = private unnamed_addr constant [2 x i8] c"a\00", align 1
|
||||||
|
@1 = private unnamed_addr constant [2 x i8] c"b\00", align 1
|
||||||
|
@2 = private unnamed_addr constant [2 x i8] c"c\00", align 1
|
||||||
|
@3 = private unnamed_addr constant [2 x i8] c"d\00", align 1
|
||||||
|
@4 = private unnamed_addr constant [2 x i8] c"1\00", align 1
|
||||||
|
@5 = private unnamed_addr constant [2 x i8] c"2\00", align 1
|
||||||
|
@6 = private unnamed_addr constant [2 x i8] c"3\00", align 1
|
||||||
|
@7 = private unnamed_addr constant [2 x i8] c"4\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 @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr @0, ptr @1, ptr @2, ptr @3)
|
||||||
|
call void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr @4, ptr @5, ptr @6, ptr @7)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr, ptr, ptr, ptr)
|
||||||
|
|
||||||
|
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||||
@@ -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])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, ...)
|
||||||
|
|||||||
20
cl/_testrt/qsort/in.go
Normal file
20
cl/_testrt/qsort/in.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/internal/runtime/c"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname qsort C.qsort
|
||||||
|
func qsort(base c.Pointer, count, elem uintptr, compar func(a, b c.Pointer) c.Int)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := [...]int{100, 8, 23, 2, 7}
|
||||||
|
qsort(c.Pointer(&a[0]), 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
71
cl/_testrt/qsort/out.ll
Normal file
71
cl/_testrt/qsort/out.ll
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
; 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
|
||||||
|
|
||||||
|
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 @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 40)
|
||||||
|
%1 = getelementptr inbounds i64, ptr %0, i64 0
|
||||||
|
%2 = getelementptr inbounds i64, ptr %0, i64 1
|
||||||
|
%3 = getelementptr inbounds i64, ptr %0, i64 2
|
||||||
|
%4 = getelementptr inbounds i64, ptr %0, i64 3
|
||||||
|
%5 = getelementptr inbounds i64, ptr %0, i64 4
|
||||||
|
store i64 100, ptr %1, align 4
|
||||||
|
store i64 8, ptr %2, align 4
|
||||||
|
store i64 23, ptr %3, align 4
|
||||||
|
store i64 2, ptr %4, align 4
|
||||||
|
store i64 7, ptr %5, align 4
|
||||||
|
%6 = getelementptr inbounds i64, ptr %0, i64 0
|
||||||
|
call void @qsort(ptr %6, i64 5, i64 8, ptr @"main.main$1")
|
||||||
|
%7 = load [5 x i64], ptr %0, align 4
|
||||||
|
br label %_llgo_1
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_2, %_llgo_0
|
||||||
|
%8 = phi i64 [ -1, %_llgo_0 ], [ %9, %_llgo_2 ]
|
||||||
|
%9 = add i64 %8, 1
|
||||||
|
%10 = icmp slt i64 %9, 5
|
||||||
|
br i1 %10, label %_llgo_2, label %_llgo_3
|
||||||
|
|
||||||
|
_llgo_2: ; preds = %_llgo_1
|
||||||
|
%11 = getelementptr inbounds i64, ptr %0, i64 %9
|
||||||
|
%12 = load i64, ptr %11, align 4
|
||||||
|
%13 = call i32 (ptr, ...) @printf(ptr @0, i64 %12)
|
||||||
|
br label %_llgo_1
|
||||||
|
|
||||||
|
_llgo_3: ; preds = %_llgo_1
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||||
|
|
||||||
|
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) {
|
||||||
|
_llgo_0:
|
||||||
|
%2 = load i64, ptr %0, align 4
|
||||||
|
%3 = load i64, ptr %1, align 4
|
||||||
|
%4 = sub i64 %2, %3
|
||||||
|
%5 = trunc i64 %4 to i32
|
||||||
|
ret i32 %5
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32 @printf(ptr, ...)
|
||||||
29
cl/_testrt/result/in.go
Normal file
29
cl/_testrt/result/in.go
Normal 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
120
cl/_testrt/result/out.ll
Normal 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
|
||||||
|
}
|
||||||
@@ -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, ...)
|
||||||
|
|||||||
@@ -9,15 +9,16 @@ source_filename = "main"
|
|||||||
define void @"(main.Foo).Print"(%main.Foo %0) {
|
define void @"(main.Foo).Print"(%main.Foo %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = alloca %main.Foo, align 8
|
%1 = alloca %main.Foo, align 8
|
||||||
store %main.Foo %0, ptr %1, align 4
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 8)
|
||||||
%2 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 1
|
store %main.Foo %0, ptr %2, align 4
|
||||||
%3 = load i1, ptr %2, align 1
|
%3 = getelementptr inbounds %main.Foo, ptr %2, i32 0, i32 1
|
||||||
br i1 %3, label %_llgo_1, label %_llgo_2
|
%4 = load i1, ptr %3, align 1
|
||||||
|
br i1 %4, label %_llgo_1, label %_llgo_2
|
||||||
|
|
||||||
_llgo_1: ; preds = %_llgo_0
|
_llgo_1: ; preds = %_llgo_0
|
||||||
%4 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 0
|
%5 = getelementptr inbounds %main.Foo, ptr %2, i32 0, i32 0
|
||||||
%5 = load i32, ptr %4, align 4
|
%6 = load i32, ptr %5, align 4
|
||||||
call void (ptr, ...) @printf(ptr @main.format, i32 %5)
|
call void (ptr, ...) @printf(ptr @main.format, i32 %6)
|
||||||
br label %_llgo_2
|
br label %_llgo_2
|
||||||
|
|
||||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||||
@@ -59,15 +60,18 @@ _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 = alloca %main.Foo, align 8
|
%0 = alloca %main.Foo, align 8
|
||||||
%1 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 0
|
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 8)
|
||||||
%2 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 0
|
||||||
store i32 100, ptr %1, align 4
|
%3 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 1
|
||||||
store i1 true, ptr %2, align 1
|
store i32 100, ptr %2, align 4
|
||||||
%3 = load %main.Foo, ptr %0, align 4
|
store i1 true, ptr %3, align 1
|
||||||
call void @"(main.Foo).Print"(%main.Foo %3)
|
%4 = load %main.Foo, ptr %1, align 4
|
||||||
|
call void @"(main.Foo).Print"(%main.Foo %4)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64)
|
||||||
|
|
||||||
declare void @printf(ptr, ...)
|
declare void @printf(ptr, ...)
|
||||||
|
|
||||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ define void @main() {
|
|||||||
_llgo_0:
|
_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.Alloc"(i64 32)
|
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||||
%1 = getelementptr inbounds i64, ptr %0, i64 0
|
%1 = getelementptr inbounds i64, ptr %0, i64 0
|
||||||
store i64 1, ptr %1, align 4
|
store i64 1, ptr %1, align 4
|
||||||
%2 = getelementptr inbounds i64, ptr %0, i64 1
|
%2 = getelementptr inbounds i64, ptr %0, i64 1
|
||||||
@@ -32,7 +32,7 @@ _llgo_0:
|
|||||||
store i64 3, ptr %3, align 4
|
store i64 3, ptr %3, align 4
|
||||||
%4 = getelementptr inbounds i64, ptr %0, i64 3
|
%4 = getelementptr inbounds i64, ptr %0, i64 3
|
||||||
store i64 4, ptr %4, align 4
|
store i64 4, ptr %4, align 4
|
||||||
%5 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 4, i64 4)
|
%5 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %0, i64 8, i64 4, i64 0, i64 4, i64 4)
|
||||||
%6 = call i64 @main.sum(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
%6 = call i64 @main.sum(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||||
%7 = call i32 (ptr, ...) @printf(ptr @0, i64 %6)
|
%7 = call i32 (ptr, ...) @printf(ptr @0, i64 %6)
|
||||||
ret void
|
ret void
|
||||||
@@ -63,9 +63,9 @@ _llgo_3: ; preds = %_llgo_1
|
|||||||
|
|
||||||
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.Alloc"(i64)
|
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||||
|
|
||||||
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr, i64, i64)
|
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
|
||||||
|
|
||||||
declare i32 @printf(ptr, ...)
|
declare i32 @printf(ptr, ...)
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ define void @main() {
|
|||||||
_llgo_0:
|
_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.Alloc"(i64 5)
|
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8)
|
||||||
%1 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 0
|
%1 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 0
|
||||||
%2 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 1
|
||||||
store i32 100, ptr %1, align 4
|
store i32 100, ptr %1, align 4
|
||||||
@@ -60,4 +60,4 @@ declare void @printf(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.Alloc"(i64)
|
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||||
|
|||||||
@@ -25,14 +25,57 @@ import (
|
|||||||
"golang.org/x/tools/go/ssa"
|
"golang.org/x/tools/go/ssa"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestErrCompileValue(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != "can't use llgo instruction as a value" {
|
||||||
|
t.Fatal("TestErrCompileValue:", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
pkg := types.NewPackage("foo", "foo")
|
||||||
|
ctx := &context{
|
||||||
|
goTyps: pkg,
|
||||||
|
link: map[string]string{
|
||||||
|
"foo.": "llgo.unreachable",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ctx.compileValue(nil, &ssa.Function{
|
||||||
|
Pkg: &ssa.Package{Pkg: pkg},
|
||||||
|
Signature: types.NewSignatureType(nil, nil, nil, nil, nil, false),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestErrCompileInstrOrValue(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Fatal("compileInstrOrValue: no error?")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ctx := &context{
|
||||||
|
bvals: make(map[ssa.Value]llssa.Expr),
|
||||||
|
}
|
||||||
|
ctx.compileInstrOrValue(nil, &ssa.Call{}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrAdvance(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Fatal("advance: no error?")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var ctx context
|
||||||
|
ctx.advance(nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func TestErrAlloca(t *testing.T) {
|
func TestErrAlloca(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r == nil {
|
if r := recover(); r == nil {
|
||||||
t.Fatal("alloca: no error?")
|
t.Fatal("alloca: no error?")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
var ctz context
|
var ctx context
|
||||||
ctz.alloca(nil, nil)
|
ctx.alloca(nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCStrNoArgs(t *testing.T) {
|
func TestCStrNoArgs(t *testing.T) {
|
||||||
@@ -122,7 +165,7 @@ func TestErrInitLinkname(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
var ctx context
|
var ctx context
|
||||||
ctx.initLinkname("foo", "//go:linkname Printf printf")
|
ctx.initLinkname("foo", "//go:linkname Printf printf", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestErrVarOf(t *testing.T) {
|
func TestErrVarOf(t *testing.T) {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
315
cl/compile.go
315
cl/compile.go
@@ -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)
|
p.compileFuncDecl(pkg, mthd.Obj().Pkg(), ssaMthd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,57 +177,115 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
|
|||||||
// Global variable.
|
// Global variable.
|
||||||
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||||
typ := gbl.Type()
|
typ := gbl.Type()
|
||||||
name := llssa.FullName(gbl.Pkg.Pkg, gbl.Name())
|
name, vtype := p.varName(gbl.Pkg.Pkg, gbl)
|
||||||
if ignoreName(name) || checkCgo(gbl.Name()) {
|
if ignoreName(name) || checkCgo(gbl.Name()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if debugInstr {
|
if debugInstr {
|
||||||
log.Println("==> NewVar", name, typ)
|
log.Println("==> NewVar", name, typ)
|
||||||
}
|
}
|
||||||
g := pkg.NewVar(name, typ)
|
g := pkg.NewVar(name, typ, llssa.Background(vtype))
|
||||||
g.Init(p.prog.Null(g.Type))
|
if vtype == goVar {
|
||||||
|
g.Init(p.prog.Null(g.Type))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) {
|
func makeClosureCtx(pkg *types.Package, vars []*ssa.FreeVar) *types.Var {
|
||||||
sig := f.Signature
|
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)
|
name, ftype := p.funcName(pkgTypes, f, true)
|
||||||
switch ftype {
|
if ftype != goFunc {
|
||||||
case ignoredFunc, llgoInstr: // llgo extended instructions
|
return nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if debugInstr {
|
fn := pkg.FuncOf(name)
|
||||||
log.Println("==> NewFunc", name, "type:", sig.Recv(), sig)
|
if fn != nil && fn.HasBody() {
|
||||||
|
return fn
|
||||||
}
|
}
|
||||||
fn := pkg.NewFunc(name, sig)
|
|
||||||
p.inits = append(p.inits, func() {
|
var sig = f.Signature
|
||||||
p.fn = fn
|
var hasCtx = len(f.FreeVars) > 0
|
||||||
defer func() {
|
if hasCtx {
|
||||||
p.fn = nil
|
|
||||||
}()
|
|
||||||
p.phis = nil
|
|
||||||
nblk := len(f.Blocks)
|
|
||||||
if nblk == 0 { // external function
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if debugGoSSA {
|
|
||||||
f.WriteTo(os.Stderr)
|
|
||||||
}
|
|
||||||
if debugInstr {
|
if debugInstr {
|
||||||
log.Println("==> FuncBody", name)
|
log.Println("==> NewClosure", name, "type:", sig)
|
||||||
}
|
}
|
||||||
fn.MakeBlocks(nblk)
|
ctx := makeClosureCtx(pkgTypes, f.FreeVars)
|
||||||
b := fn.NewBuilder()
|
sig = llssa.FuncAddCtx(ctx, sig)
|
||||||
p.bvals = make(map[ssa.Value]llssa.Expr)
|
} else {
|
||||||
for i, block := range f.Blocks {
|
if debugInstr {
|
||||||
p.compileBlock(b, block, i == 0 && name == "main")
|
log.Println("==> NewFunc", name, "type:", sig.Recv(), sig)
|
||||||
}
|
}
|
||||||
for _, phi := range p.phis {
|
}
|
||||||
phi()
|
if fn == nil {
|
||||||
}
|
fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx)
|
||||||
})
|
}
|
||||||
|
if nblk := len(f.Blocks); nblk > 0 {
|
||||||
|
fn.MakeBlocks(nblk) // to set fn.HasBody() = true
|
||||||
|
p.inits = append(p.inits, func() {
|
||||||
|
p.fn = fn
|
||||||
|
defer func() {
|
||||||
|
p.fn = nil
|
||||||
|
}()
|
||||||
|
p.phis = nil
|
||||||
|
if debugGoSSA {
|
||||||
|
f.WriteTo(os.Stderr)
|
||||||
|
}
|
||||||
|
if debugInstr {
|
||||||
|
log.Println("==> FuncBody", name)
|
||||||
|
}
|
||||||
|
b := fn.NewBuilder()
|
||||||
|
p.bvals = make(map[ssa.Value]llssa.Expr)
|
||||||
|
off := make([]int, len(f.Blocks))
|
||||||
|
for i, block := range f.Blocks {
|
||||||
|
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 {
|
||||||
|
phi()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
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 {
|
||||||
@@ -235,7 +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)
|
||||||
}
|
}
|
||||||
for _, instr := range block.Instrs {
|
for _, instr := range block.Instrs[n:] {
|
||||||
p.compileInstr(b, instr)
|
p.compileInstr(b, instr)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
@@ -246,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,6 +357,15 @@ func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
|||||||
panic("cstr(<string-literal>): invalid arguments")
|
panic("cstr(<string-literal>): invalid arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *context) advance(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
|
if len(args) == 2 {
|
||||||
|
ptr := p.compileValue(b, args[0])
|
||||||
|
offset := p.compileValue(b, args[1])
|
||||||
|
return b.Advance(ptr, offset)
|
||||||
|
}
|
||||||
|
panic("advance(p ptr, offset int): invalid arguments")
|
||||||
|
}
|
||||||
|
|
||||||
// func alloca(size uintptr) unsafe.Pointer
|
// func alloca(size uintptr) unsafe.Pointer
|
||||||
func (p *context) alloca(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
func (p *context) alloca(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
@@ -316,6 +384,52 @@ func (p *context) allocaCStr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr)
|
|||||||
panic("allocaCStr(s string): invalid arguments")
|
panic("allocaCStr(s string): invalid arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isPhi(i ssa.Instruction) bool {
|
||||||
|
_, ok := i.(*ssa.Phi)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *context) compilePhis(b llssa.Builder, block *ssa.BasicBlock) int {
|
||||||
|
ret := p.fn.Block(block.Index)
|
||||||
|
b.SetBlock(ret)
|
||||||
|
if ninstr := len(block.Instrs); ninstr > 0 {
|
||||||
|
if isPhi(block.Instrs[0]) {
|
||||||
|
n := 1
|
||||||
|
for n < ninstr && isPhi(block.Instrs[n]) {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
rets := make([]llssa.Expr, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
iv := block.Instrs[i].(*ssa.Phi)
|
||||||
|
rets[i] = p.compilePhi(b, iv)
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
iv := block.Instrs[i].(*ssa.Phi)
|
||||||
|
p.bvals[iv] = rets[i].Do(b)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *context) compilePhi(b llssa.Builder, v *ssa.Phi) (ret llssa.Expr) {
|
||||||
|
phi := b.Phi(p.prog.Type(v.Type(), llssa.InGo))
|
||||||
|
ret = phi.Expr
|
||||||
|
p.phis = append(p.phis, func() {
|
||||||
|
preds := v.Block().Preds
|
||||||
|
bblks := make([]llssa.BasicBlock, len(preds))
|
||||||
|
for i, pred := range preds {
|
||||||
|
bblks[i] = p.fn.Block(pred.Index)
|
||||||
|
}
|
||||||
|
edges := v.Edges
|
||||||
|
phi.AddIncoming(b, bblks, func(i int) llssa.Expr {
|
||||||
|
return p.compileValue(b, edges[i])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) {
|
func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) {
|
||||||
if asValue {
|
if asValue {
|
||||||
if v, ok := p.bvals[iv]; ok {
|
if v, ok := p.bvals[iv]; ok {
|
||||||
@@ -346,13 +460,15 @@ 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)
|
||||||
ret = b.Call(fn.Expr, args...)
|
ret = b.Call(fn.Expr, args...)
|
||||||
case llgoCstr:
|
case llgoCstr:
|
||||||
ret = cstr(b, call.Args)
|
ret = cstr(b, call.Args)
|
||||||
|
case llgoAdvance:
|
||||||
|
ret = p.advance(b, call.Args)
|
||||||
case llgoAlloca:
|
case llgoAlloca:
|
||||||
ret = p.alloca(b, call.Args)
|
ret = p.alloca(b, call.Args)
|
||||||
case llgoAllocaCStr:
|
case llgoAllocaCStr:
|
||||||
@@ -363,12 +479,9 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
|||||||
panic("todo")
|
panic("todo")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("todo")
|
fn := p.compileValue(b, cv)
|
||||||
/*
|
args := p.compileValues(b, call.Args, kind)
|
||||||
fn := p.compileValue(b, cv)
|
ret = b.Call(fn, args...)
|
||||||
args := p.compileValues(b, call.Args, kind)
|
|
||||||
ret = b.Call(fn, args...)
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
case *ssa.BinOp:
|
case *ssa.BinOp:
|
||||||
x := p.compileValue(b, v.X)
|
x := p.compileValue(b, v.X)
|
||||||
@@ -377,26 +490,14 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
|||||||
case *ssa.UnOp:
|
case *ssa.UnOp:
|
||||||
x := p.compileValue(b, v.X)
|
x := p.compileValue(b, v.X)
|
||||||
ret = b.UnOp(v.Op, x)
|
ret = b.UnOp(v.Op, x)
|
||||||
case *ssa.Phi:
|
|
||||||
phi := b.Phi(p.prog.Type(v.Type()))
|
|
||||||
ret = phi.Expr
|
|
||||||
p.phis = append(p.phis, func() {
|
|
||||||
vals := p.compileValues(b, v.Edges, 0)
|
|
||||||
preds := v.Block().Preds
|
|
||||||
bblks := make([]llssa.BasicBlock, len(preds))
|
|
||||||
for i, pred := range preds {
|
|
||||||
bblks[i] = p.fn.Block(pred.Index)
|
|
||||||
}
|
|
||||||
phi.AddIncoming(vals, bblks)
|
|
||||||
})
|
|
||||||
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)
|
||||||
@@ -405,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
|
||||||
@@ -414,6 +516,21 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
|||||||
x := p.compileValue(b, vx)
|
x := p.compileValue(b, vx)
|
||||||
idx := p.compileValue(b, v.Index)
|
idx := p.compileValue(b, v.Index)
|
||||||
ret = b.IndexAddr(x, idx)
|
ret = b.IndexAddr(x, idx)
|
||||||
|
case *ssa.Index:
|
||||||
|
x := p.compileValue(b, v.X)
|
||||||
|
idx := p.compileValue(b, v.Index)
|
||||||
|
ret = b.Index(x, idx, func(e llssa.Expr) (ret llssa.Expr) {
|
||||||
|
if e == x {
|
||||||
|
if n, ok := v.X.(*ssa.UnOp); ok {
|
||||||
|
return p.compileValue(b, n.X)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
@@ -431,23 +548,39 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
|||||||
max = p.compileValue(b, v.Max)
|
max = p.compileValue(b, v.Max)
|
||||||
}
|
}
|
||||||
ret = b.Slice(x, low, high, max)
|
ret = b.Slice(x, low, high, max)
|
||||||
case *ssa.MakeMap:
|
|
||||||
var nReserve llssa.Expr
|
|
||||||
t := v.Type()
|
|
||||||
if v.Reserve != nil {
|
|
||||||
nReserve = p.compileValue(b, v.Reserve)
|
|
||||||
}
|
|
||||||
ret = b.MakeMap(p.prog.Type(t), nReserve)
|
|
||||||
case *ssa.MakeInterface:
|
case *ssa.MakeInterface:
|
||||||
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:
|
||||||
|
var nCap llssa.Expr
|
||||||
|
t := p.prog.Type(v.Type(), llssa.InGo)
|
||||||
|
nLen := p.compileValue(b, v.Len)
|
||||||
|
if v.Cap != nil {
|
||||||
|
nCap = p.compileValue(b, v.Cap)
|
||||||
|
}
|
||||||
|
ret = b.MakeSlice(t, nLen, nCap)
|
||||||
|
case *ssa.MakeMap:
|
||||||
|
var nReserve llssa.Expr
|
||||||
|
t := p.prog.Type(v.Type(), llssa.InGo)
|
||||||
|
if v.Reserve != nil {
|
||||||
|
nReserve = p.compileValue(b, v.Reserve)
|
||||||
|
}
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
@@ -504,13 +637,24 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
|
|||||||
val := p.compileValue(b, v.Value)
|
val := p.compileValue(b, v.Value)
|
||||||
b.MapUpdate(m, key, val)
|
b.MapUpdate(m, key, val)
|
||||||
case *ssa.Panic:
|
case *ssa.Panic:
|
||||||
arg := p.compileValue(b, v.X).Do()
|
arg := p.compileValue(b, v.X).Do(b)
|
||||||
b.Panic(arg)
|
b.Panic(arg)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr))
|
panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -524,20 +668,21 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *ssa.Function:
|
case *ssa.Function:
|
||||||
panic("unreachable")
|
fn, _ := p.compileFunction(v)
|
||||||
/*
|
return fn.Expr
|
||||||
fn, ftype := p.funcOf(v)
|
|
||||||
if ftype >= llgoInstrBase {
|
|
||||||
panic("can't use llgo instruction as a value")
|
|
||||||
}
|
|
||||||
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))
|
||||||
}
|
}
|
||||||
@@ -561,7 +706,7 @@ func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int)
|
|||||||
n := len(vals) - hasVArg
|
n := len(vals) - hasVArg
|
||||||
ret := make([]llssa.Expr, n)
|
ret := make([]llssa.Expr, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
ret[i] = p.compileValue(b, vals[i]).Do()
|
ret[i] = p.compileValue(b, vals[i]).Do(b)
|
||||||
}
|
}
|
||||||
if hasVArg > 0 {
|
if hasVArg > 0 {
|
||||||
ret = p.compileVArg(ret, b, vals[n])
|
ret = p.compileVArg(ret, b, vals[n])
|
||||||
@@ -616,15 +761,19 @@ 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)
|
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:
|
||||||
ctx.compileGlobal(ret, member)
|
ctx.compileGlobal(ret, member)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, ini := range ctx.inits {
|
for len(ctx.inits) > 0 {
|
||||||
ini()
|
inits := ctx.inits
|
||||||
|
ctx.inits = nil
|
||||||
|
for _, ini := range inits {
|
||||||
|
ini()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
110
cl/import.go
110
cl/import.go
@@ -90,19 +90,14 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) {
|
|||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if token.IsExported(name) {
|
if token.IsExported(name) {
|
||||||
obj := scope.Lookup(name)
|
obj := scope.Lookup(name)
|
||||||
if obj, ok := obj.(*types.Func); ok {
|
switch obj := obj.(type) {
|
||||||
|
case *types.Func:
|
||||||
if pos := obj.Pos(); pos != token.NoPos {
|
if pos := obj.Pos(); pos != token.NoPos {
|
||||||
f := fset.File(pos)
|
p.initLinknameByPos(fset, pos, pkgPath, contents, false)
|
||||||
if fp := f.Position(pos); fp.Line > 2 {
|
}
|
||||||
lines, err := contentOf(contents, fp.Filename)
|
case *types.Var:
|
||||||
if err != nil {
|
if pos := obj.Pos(); pos != token.NoPos {
|
||||||
panic(err)
|
p.initLinknameByPos(fset, pos, pkgPath, contents, true)
|
||||||
}
|
|
||||||
if i := fp.Line - 2; i < len(lines) {
|
|
||||||
line := string(lines[i])
|
|
||||||
p.initLinkname(pkgPath, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,21 +107,44 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) {
|
|||||||
func (p *context) initFiles(pkgPath string, files []*ast.File) {
|
func (p *context) initFiles(pkgPath string, files []*ast.File) {
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
for _, decl := range file.Decls {
|
for _, decl := range file.Decls {
|
||||||
if decl, ok := decl.(*ast.FuncDecl); ok {
|
switch decl := decl.(type) {
|
||||||
|
case *ast.FuncDecl:
|
||||||
if decl.Recv == nil {
|
if decl.Recv == nil {
|
||||||
if doc := decl.Doc; doc != nil {
|
p.initLinknameByDoc(decl.Doc, pkgPath, false)
|
||||||
if n := len(doc.List); n > 0 {
|
}
|
||||||
line := doc.List[n-1].Text
|
case *ast.GenDecl:
|
||||||
p.initLinkname(pkgPath, line)
|
if decl.Tok == token.VAR && len(decl.Specs) == 1 {
|
||||||
}
|
p.initLinknameByDoc(decl.Doc, pkgPath, true)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *context) initLinkname(pkgPath, line string) {
|
func (p *context) initLinknameByDoc(doc *ast.CommentGroup, pkgPath string, isVar bool) {
|
||||||
|
if doc != nil {
|
||||||
|
if n := len(doc.List); n > 0 {
|
||||||
|
line := doc.List[n-1].Text
|
||||||
|
p.initLinkname(pkgPath, line, isVar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *context) initLinknameByPos(fset *token.FileSet, pos token.Pos, pkgPath string, contents contentMap, isVar bool) {
|
||||||
|
f := fset.File(pos)
|
||||||
|
if fp := f.Position(pos); fp.Line > 2 {
|
||||||
|
lines, err := contentOf(contents, fp.Filename)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if i := fp.Line - 2; i < len(lines) {
|
||||||
|
line := string(lines[i])
|
||||||
|
p.initLinkname(pkgPath, line, isVar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *context) initLinkname(pkgPath, line string, isVar bool) {
|
||||||
const (
|
const (
|
||||||
linkname = "//go:linkname "
|
linkname = "//go:linkname "
|
||||||
)
|
)
|
||||||
@@ -134,7 +152,7 @@ func (p *context) initLinkname(pkgPath, line string) {
|
|||||||
text := strings.TrimSpace(line[len(linkname):])
|
text := strings.TrimSpace(line[len(linkname):])
|
||||||
if idx := strings.IndexByte(text, ' '); idx > 0 {
|
if idx := strings.IndexByte(text, ' '); idx > 0 {
|
||||||
link := strings.TrimLeft(text[idx+1:], " ")
|
link := strings.TrimLeft(text[idx+1:], " ")
|
||||||
if strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr
|
if isVar || strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr
|
||||||
name := pkgPath + "." + text[:idx]
|
name := pkgPath + "." + text[:idx]
|
||||||
p.link[name] = link
|
p.link[name] = link
|
||||||
} else {
|
} else {
|
||||||
@@ -173,15 +191,16 @@ 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
|
||||||
llgoUnreachable = llgoInstrBase + 0
|
llgoUnreachable = llgoInstrBase + 0
|
||||||
llgoCstr = llgoInstrBase + 1
|
llgoCstr = llgoInstrBase + 1
|
||||||
llgoAlloca = llgoInstrBase + 2
|
llgoAlloca = llgoInstrBase + 2
|
||||||
llgoAllocaCStr = llgoInstrBase + 3
|
llgoAllocaCStr = llgoInstrBase + 3
|
||||||
|
llgoAdvance = llgoInstrBase + 4
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, int) {
|
func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, int) {
|
||||||
@@ -201,39 +220,28 @@ func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (s
|
|||||||
return name, goFunc
|
return name, goFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
|
const (
|
||||||
// or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc.
|
ignoredVar = iota
|
||||||
func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
|
goVar = int(llssa.InGo)
|
||||||
pkgTypes := p.ensureLoaded(fn.Pkg.Pkg)
|
cVar = int(llssa.InC)
|
||||||
pkg := p.pkg
|
)
|
||||||
name, ftype := p.funcName(pkgTypes, fn, false)
|
|
||||||
if ftype == llgoInstr {
|
func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, vtype int) {
|
||||||
switch name {
|
name := llssa.FullName(pkg, v.Name())
|
||||||
case "cstr":
|
if v, ok := p.link[name]; ok {
|
||||||
ftype = llgoCstr
|
return v, cVar
|
||||||
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
|
return name, goVar
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *context) varOf(v *ssa.Global) 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 := llssa.FullName(pkgTypes, v.Name())
|
name, vtype := p.varName(pkgTypes, v)
|
||||||
if ret := pkg.VarOf(name); ret != nil {
|
if ret = pkg.VarOf(name); ret == nil {
|
||||||
return ret
|
ret = pkg.NewVar(name, v.Type(), llssa.Background(vtype))
|
||||||
}
|
}
|
||||||
return pkg.NewVar(name, v.Type())
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package {
|
func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package {
|
||||||
|
|||||||
9
cl/internal/linktarget/foo.go
Normal file
9
cl/internal/linktarget/foo.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package linktarget
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/goplus/llgo/internal/runtime/c"
|
||||||
|
)
|
||||||
|
|
||||||
|
func F(a, b *c.Char) {
|
||||||
|
c.Printf(c.Str("a: %s, b: %s\n"), a, b)
|
||||||
|
}
|
||||||
26
cl/internal/linktarget/out.ll
Normal file
26
cl/internal/linktarget/out.ll
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
; ModuleID = 'github.com/goplus/llgo/cl/internal/linktarget'
|
||||||
|
source_filename = "github.com/goplus/llgo/cl/internal/linktarget"
|
||||||
|
|
||||||
|
@"github.com/goplus/llgo/cl/internal/linktarget.init$guard" = global ptr null
|
||||||
|
@0 = private unnamed_addr constant [14 x i8] c"a: %s, b: %s\0A\00", align 1
|
||||||
|
|
||||||
|
define void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr %0, ptr %1) {
|
||||||
|
_llgo_0:
|
||||||
|
%2 = call i32 (ptr, ...) @printf(ptr @0, ptr %0, ptr %1)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @"github.com/goplus/llgo/cl/internal/linktarget.init"() {
|
||||||
|
_llgo_0:
|
||||||
|
%0 = load i1, ptr @"github.com/goplus/llgo/cl/internal/linktarget.init$guard", align 1
|
||||||
|
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_0
|
||||||
|
store i1 true, ptr @"github.com/goplus/llgo/cl/internal/linktarget.init$guard", align 1
|
||||||
|
br label %_llgo_2
|
||||||
|
|
||||||
|
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32 @printf(ptr, ...)
|
||||||
5
go.mod
5
go.mod
@@ -5,13 +5,16 @@ go 1.18
|
|||||||
require (
|
require (
|
||||||
github.com/aykevl/go-wasm v0.0.1
|
github.com/aykevl/go-wasm v0.0.1
|
||||||
github.com/goplus/gogen v1.15.2
|
github.com/goplus/gogen v1.15.2
|
||||||
github.com/goplus/llvm v0.7.3
|
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
|
||||||
)
|
)
|
||||||
|
|||||||
16
go.sum
16
go.sum
@@ -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.3 h1:I7UkAO4kzn0Es2iHKRpGU1LjYQ452XwYfsSs1OAAXk8=
|
github.com/goplus/llvm v0.7.5 h1:ges8WcUdu4FBi0mkZUs27p/4qDQlj28N1UpMg3VQUoE=
|
||||||
github.com/goplus/llvm v0.7.3/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=
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
source_filename = "github.com/goplus/llgo/internal/abi"
|
source_filename = "github.com/goplus/llgo/internal/abi"
|
||||||
|
|
||||||
%"github.com/goplus/llgo/internal/abi.ArrayType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, i64 }
|
%"github.com/goplus/llgo/internal/abi.ArrayType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, i64 }
|
||||||
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
|
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, { ptr, ptr }, ptr, i32, i32 }
|
||||||
%"github.com/goplus/llgo/internal/abi.ChanType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, i64 }
|
%"github.com/goplus/llgo/internal/abi.ChanType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, i64 }
|
||||||
%"github.com/goplus/llgo/internal/abi.FuncType" = type { %"github.com/goplus/llgo/internal/abi.Type", i16, i16 }
|
%"github.com/goplus/llgo/internal/abi.FuncType" = type { %"github.com/goplus/llgo/internal/abi.Type", i16, i16 }
|
||||||
%"github.com/goplus/llgo/internal/abi.InterfaceType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" }
|
%"github.com/goplus/llgo/internal/abi.InterfaceType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" }
|
||||||
%"github.com/goplus/llgo/internal/abi.Name" = type { ptr }
|
%"github.com/goplus/llgo/internal/abi.Name" = type { ptr }
|
||||||
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||||
%"github.com/goplus/llgo/internal/abi.MapType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, ptr, ptr, i8, i8, i16, i32 }
|
%"github.com/goplus/llgo/internal/abi.MapType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, ptr, { ptr, ptr }, i8, i8, i16, i32 }
|
||||||
%"github.com/goplus/llgo/internal/abi.PtrType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr }
|
%"github.com/goplus/llgo/internal/abi.PtrType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr }
|
||||||
%"github.com/goplus/llgo/internal/abi.SliceType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr }
|
%"github.com/goplus/llgo/internal/abi.SliceType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr }
|
||||||
%"github.com/goplus/llgo/internal/abi.StructType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" }
|
%"github.com/goplus/llgo/internal/abi.StructType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" }
|
||||||
|
|||||||
32
internal/aliases/aliases.go
Normal file
32
internal/aliases/aliases.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package aliases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Package aliases defines backward compatible shims
|
||||||
|
// for the types.Alias type representation added in 1.22.
|
||||||
|
// This defines placeholders for x/tools until 1.26.
|
||||||
|
|
||||||
|
// NewAlias creates a new TypeName in Package pkg that
|
||||||
|
// is an alias for the type rhs.
|
||||||
|
//
|
||||||
|
// The enabled parameter determines whether the resulting [TypeName]'s
|
||||||
|
// type is an [types.Alias]. Its value must be the result of a call to
|
||||||
|
// [Enabled], which computes the effective value of
|
||||||
|
// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled
|
||||||
|
// function is expensive and should be called once per task (e.g.
|
||||||
|
// package import), not once per call to NewAlias.
|
||||||
|
func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName {
|
||||||
|
if enabled {
|
||||||
|
tname := types.NewTypeName(pos, pkg, name, nil)
|
||||||
|
newAlias(tname, rhs)
|
||||||
|
return tname
|
||||||
|
}
|
||||||
|
return types.NewTypeName(pos, pkg, name, rhs)
|
||||||
|
}
|
||||||
31
internal/aliases/aliases_go121.go
Normal file
31
internal/aliases/aliases_go121.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !go1.22
|
||||||
|
// +build !go1.22
|
||||||
|
|
||||||
|
package aliases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Alias is a placeholder for a go/types.Alias for <=1.21.
|
||||||
|
// It will never be created by go/types.
|
||||||
|
type Alias struct{}
|
||||||
|
|
||||||
|
func (*Alias) String() string { panic("unreachable") }
|
||||||
|
func (*Alias) Underlying() types.Type { panic("unreachable") }
|
||||||
|
func (*Alias) Obj() *types.TypeName { panic("unreachable") }
|
||||||
|
func Rhs(alias *Alias) types.Type { panic("unreachable") }
|
||||||
|
|
||||||
|
// Unalias returns the type t for go <=1.21.
|
||||||
|
func Unalias(t types.Type) types.Type { return t }
|
||||||
|
|
||||||
|
func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") }
|
||||||
|
|
||||||
|
// Enabled reports whether [NewAlias] should create [types.Alias] types.
|
||||||
|
//
|
||||||
|
// Before go1.22, this function always returns false.
|
||||||
|
func Enabled() bool { return false }
|
||||||
63
internal/aliases/aliases_go122.go
Normal file
63
internal/aliases/aliases_go122.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build go1.22
|
||||||
|
// +build go1.22
|
||||||
|
|
||||||
|
package aliases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Alias is an alias of types.Alias.
|
||||||
|
type Alias = types.Alias
|
||||||
|
|
||||||
|
// Rhs returns the type on the right-hand side of the alias declaration.
|
||||||
|
func Rhs(alias *Alias) types.Type {
|
||||||
|
if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok {
|
||||||
|
return alias.Rhs() // go1.23+
|
||||||
|
}
|
||||||
|
|
||||||
|
// go1.22's Alias didn't have the Rhs method,
|
||||||
|
// so Unalias is the best we can do.
|
||||||
|
return Unalias(alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unalias is a wrapper of types.Unalias.
|
||||||
|
func Unalias(t types.Type) types.Type { return types.Unalias(t) }
|
||||||
|
|
||||||
|
// newAlias is an internal alias around types.NewAlias.
|
||||||
|
// Direct usage is discouraged as the moment.
|
||||||
|
// Try to use NewAlias instead.
|
||||||
|
func newAlias(tname *types.TypeName, rhs types.Type) *Alias {
|
||||||
|
a := types.NewAlias(tname, rhs)
|
||||||
|
// TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect.
|
||||||
|
Unalias(a)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled reports whether [NewAlias] should create [types.Alias] types.
|
||||||
|
//
|
||||||
|
// This function is expensive! Call it sparingly.
|
||||||
|
func Enabled() bool {
|
||||||
|
// The only reliable way to compute the answer is to invoke go/types.
|
||||||
|
// We don't parse the GODEBUG environment variable, because
|
||||||
|
// (a) it's tricky to do so in a manner that is consistent
|
||||||
|
// with the godebug package; in particular, a simple
|
||||||
|
// substring check is not good enough. The value is a
|
||||||
|
// rightmost-wins list of options. But more importantly:
|
||||||
|
// (b) it is impossible to detect changes to the effective
|
||||||
|
// setting caused by os.Setenv("GODEBUG"), as happens in
|
||||||
|
// many tests. Therefore any attempt to cache the result
|
||||||
|
// is just incorrect.
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
|
||||||
|
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
|
||||||
|
_, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias)
|
||||||
|
return enabled
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -27,11 +27,24 @@ type (
|
|||||||
Char = int8
|
Char = int8
|
||||||
Int = C.int
|
Int = C.int
|
||||||
Pointer = unsafe.Pointer
|
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
|
//go:linkname Str llgo.cstr
|
||||||
func Str(string) *Char
|
func Str(string) *Char
|
||||||
|
|
||||||
|
//go:linkname Advance llgo.advance
|
||||||
|
func Advance(ptr Pointer, offset int) Pointer
|
||||||
|
|
||||||
//go:linkname Alloca llgo.alloca
|
//go:linkname Alloca llgo.alloca
|
||||||
func Alloca(size uintptr) Pointer
|
func Alloca(size uintptr) Pointer
|
||||||
|
|
||||||
@@ -50,5 +63,11 @@ func Malloc(size uintptr) Pointer
|
|||||||
//go:linkname Memcpy C.memcpy
|
//go:linkname Memcpy C.memcpy
|
||||||
func Memcpy(dst, src Pointer, n uintptr) Pointer
|
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
|
//go:linkname Printf C.printf
|
||||||
func Printf(format *Char, __llgo_va_list ...any) Int
|
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
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ source_filename = "github.com/goplus/llgo/internal/runtime"
|
|||||||
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
|
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
|
||||||
%"github.com/goplus/llgo/internal/runtime.itab" = type { ptr, ptr, i32, [4 x i8], [1 x i64] }
|
%"github.com/goplus/llgo/internal/runtime.itab" = type { ptr, ptr, i32, [4 x i8], [1 x i64] }
|
||||||
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||||
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
|
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, { ptr, ptr }, ptr, i32, i32 }
|
||||||
%"github.com/goplus/llgo/internal/runtime.hmap" = type { i64, i8, i8, i16, i32, ptr, ptr, i64, ptr }
|
%"github.com/goplus/llgo/internal/runtime.hmap" = type { i64, i8, i8, i16, i32, ptr, ptr, i64, ptr }
|
||||||
|
|
||||||
@"github.com/goplus/llgo/internal/runtime.TyAny" = global ptr null
|
@"github.com/goplus/llgo/internal/runtime.TyAny" = global ptr null
|
||||||
@@ -13,14 +13,24 @@ source_filename = "github.com/goplus/llgo/internal/runtime"
|
|||||||
@"github.com/goplus/llgo/internal/runtime.init$guard" = global ptr null
|
@"github.com/goplus/llgo/internal/runtime.init$guard" = global ptr null
|
||||||
@"github.com/goplus/llgo/internal/runtime.sizeBasicTypes" = global ptr null
|
@"github.com/goplus/llgo/internal/runtime.sizeBasicTypes" = global ptr null
|
||||||
@0 = private unnamed_addr constant [21 x i8] c"I2Int: type mismatch\00", align 1
|
@0 = private unnamed_addr constant [21 x i8] c"I2Int: type mismatch\00", align 1
|
||||||
@1 = private unnamed_addr constant [11 x i8] c"panic: %s\0A\00", align 1
|
@1 = private unnamed_addr constant [26 x i8] c"slice index out of bounds\00", align 1
|
||||||
|
@2 = private unnamed_addr constant [33 x i8] c"string slice index out of bounds\00", align 1
|
||||||
|
@__stderrp = external global ptr
|
||||||
|
@3 = private unnamed_addr constant [11 x i8] c"panic: %s\0A\00", align 1
|
||||||
|
|
||||||
define ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 %0) {
|
define ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = call ptr @malloc(i64 %0)
|
%1 = call ptr @malloc(i64 %0)
|
||||||
ret ptr %1
|
ret ptr %1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 %0) {
|
||||||
|
_llgo_0:
|
||||||
|
%1 = call ptr @malloc(i64 %0)
|
||||||
|
%2 = call ptr @memset(ptr %1, i32 0, i64 %0)
|
||||||
|
ret ptr %2
|
||||||
|
}
|
||||||
|
|
||||||
define ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 %0) {
|
define ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = getelementptr inbounds ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 %0
|
%1 = getelementptr inbounds ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 %0
|
||||||
@@ -31,46 +41,49 @@ _llgo_0:
|
|||||||
define ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %0, %"github.com/goplus/llgo/internal/runtime.String" %1) {
|
define ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %0, %"github.com/goplus/llgo/internal/runtime.String" %1) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
%2 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
store %"github.com/goplus/llgo/internal/runtime.String" %1, ptr %2, align 8
|
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
|
||||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1
|
store %"github.com/goplus/llgo/internal/runtime.String" %1, ptr %3, align 8
|
||||||
%4 = load i64, ptr %3, align 4
|
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
|
||||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0
|
%5 = load i64, ptr %4, align 4
|
||||||
%6 = load ptr, ptr %5, align 8
|
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 0
|
||||||
%7 = call ptr @memcpy(ptr %0, ptr %6, i64 %4)
|
%7 = load ptr, ptr %6, align 8
|
||||||
%8 = getelementptr inbounds i8, ptr %0, i64 %4
|
%8 = call ptr @memcpy(ptr %0, ptr %7, i64 %5)
|
||||||
store i8 0, ptr %8, align 1
|
%9 = getelementptr inbounds i8, ptr %0, i64 %5
|
||||||
|
store i8 0, ptr %9, align 1
|
||||||
ret ptr %0
|
ret ptr %0
|
||||||
}
|
}
|
||||||
|
|
||||||
define ptr @"github.com/goplus/llgo/internal/runtime.CStrDup"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
define ptr @"github.com/goplus/llgo/internal/runtime.CStrDup"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
%1 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %1, align 8
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 16)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 1
|
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %2, align 8
|
||||||
%3 = load i64, ptr %2, align 4
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1
|
||||||
%4 = add i64 %3, 1
|
%4 = load i64, ptr %3, align 4
|
||||||
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 %4)
|
%5 = add i64 %4, 1
|
||||||
%6 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %1, align 8
|
%6 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 %5)
|
||||||
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %5, %"github.com/goplus/llgo/internal/runtime.String" %6)
|
%7 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %2, align 8
|
||||||
ret ptr %7
|
%8 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %6, %"github.com/goplus/llgo/internal/runtime.String" %7)
|
||||||
|
ret ptr %8
|
||||||
}
|
}
|
||||||
|
|
||||||
define { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
|
define { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||||
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %2, align 8
|
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
|
||||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
|
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %3, align 8
|
||||||
%4 = load ptr, ptr %3, align 8
|
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 0
|
||||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
|
%5 = load ptr, ptr %4, align 8
|
||||||
%6 = load ptr, ptr %5, align 8
|
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 1
|
||||||
%7 = icmp eq ptr %6, %1
|
%7 = load ptr, ptr %6, align 8
|
||||||
br i1 %7, label %_llgo_1, label %_llgo_2
|
%8 = icmp eq ptr %7, %1
|
||||||
|
br i1 %8, label %_llgo_1, label %_llgo_2
|
||||||
|
|
||||||
_llgo_1: ; preds = %_llgo_0
|
_llgo_1: ; preds = %_llgo_0
|
||||||
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
|
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 1
|
||||||
%9 = load ptr, ptr %8, align 8
|
%10 = load ptr, ptr %9, align 8
|
||||||
%10 = ptrtoint ptr %9 to i64
|
%11 = ptrtoint ptr %10 to i64
|
||||||
%mrv = insertvalue { i64, i1 } poison, i64 %10, 0
|
%mrv = insertvalue { i64, i1 } poison, i64 %11, 0
|
||||||
%mrv1 = insertvalue { i64, i1 } %mrv, i1 true, 1
|
%mrv1 = insertvalue { i64, i1 } %mrv, i1 true, 1
|
||||||
ret { i64, i1 } %mrv1
|
ret { i64, i1 } %mrv1
|
||||||
|
|
||||||
@@ -81,41 +94,43 @@ _llgo_2: ; preds = %_llgo_0
|
|||||||
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.EmptyString"() {
|
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.EmptyString"() {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%0 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
%0 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 0
|
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 16)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 0
|
||||||
store ptr null, ptr %1, align 8
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 1
|
||||||
store i64 0, ptr %2, align 4
|
store ptr null, ptr %2, align 8
|
||||||
%3 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %0, align 8
|
store i64 0, ptr %3, align 4
|
||||||
ret %"github.com/goplus/llgo/internal/runtime.String" %3
|
%4 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %1, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.String" %4
|
||||||
}
|
}
|
||||||
|
|
||||||
define i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
|
define i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||||
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %2, align 8
|
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
|
||||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
|
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %3, align 8
|
||||||
%4 = load ptr, ptr %3, align 8
|
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 0
|
||||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
|
%5 = load ptr, ptr %4, align 8
|
||||||
%6 = load ptr, ptr %5, align 8
|
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 1
|
||||||
%7 = icmp eq ptr %6, %1
|
%7 = load ptr, ptr %6, align 8
|
||||||
br i1 %7, label %_llgo_1, label %_llgo_2
|
%8 = icmp eq ptr %7, %1
|
||||||
|
br i1 %8, label %_llgo_1, label %_llgo_2
|
||||||
|
|
||||||
_llgo_1: ; preds = %_llgo_0
|
_llgo_1: ; preds = %_llgo_0
|
||||||
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
|
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 1
|
||||||
%9 = load ptr, ptr %8, align 8
|
%10 = load ptr, ptr %9, align 8
|
||||||
%10 = ptrtoint ptr %9 to i64
|
%11 = ptrtoint ptr %10 to i64
|
||||||
ret i64 %10
|
ret i64 %11
|
||||||
|
|
||||||
_llgo_2: ; preds = %_llgo_0
|
_llgo_2: ; preds = %_llgo_0
|
||||||
%11 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 20)
|
%12 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 20)
|
||||||
%12 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %11)
|
%13 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %12)
|
||||||
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %12)
|
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %13)
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAny"(ptr %0, ptr %1) {
|
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAny"(ptr %0, ptr %1) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 0
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 0
|
||||||
%4 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
%4 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
||||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 1
|
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 1
|
||||||
@@ -127,17 +142,18 @@ _llgo_0:
|
|||||||
store i32 0, ptr %6, align 4
|
store i32 0, ptr %6, align 4
|
||||||
store i64 0, ptr %8, align 4
|
store i64 0, ptr %8, align 4
|
||||||
%9 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
%9 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 0
|
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %9, i64 16)
|
||||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 1
|
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 0
|
||||||
store ptr %2, ptr %10, align 8
|
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 1
|
||||||
store ptr %1, ptr %11, align 8
|
store ptr %2, ptr %11, align 8
|
||||||
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, align 8
|
store ptr %1, ptr %12, align 8
|
||||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
|
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
|
||||||
}
|
}
|
||||||
|
|
||||||
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %0, i64 %1) {
|
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %0, i64 %1) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 0
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 0
|
||||||
%4 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
%4 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
||||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 1
|
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 1
|
||||||
@@ -149,21 +165,22 @@ _llgo_0:
|
|||||||
store i32 0, ptr %6, align 4
|
store i32 0, ptr %6, align 4
|
||||||
store i64 0, ptr %8, align 4
|
store i64 0, ptr %8, align 4
|
||||||
%9 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
%9 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 0
|
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %9, i64 16)
|
||||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 1
|
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 0
|
||||||
%12 = inttoptr i64 %1 to ptr
|
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 1
|
||||||
store ptr %2, ptr %10, align 8
|
%13 = inttoptr i64 %1 to ptr
|
||||||
store ptr %12, ptr %11, align 8
|
store ptr %2, ptr %11, align 8
|
||||||
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, align 8
|
store ptr %13, ptr %12, align 8
|
||||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
|
%14 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.iface" %14
|
||||||
}
|
}
|
||||||
|
|
||||||
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
|
%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
|
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %1, align 8
|
||||||
%2 = load ptr, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 24), align 8
|
%2 = load ptr, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 24), align 8
|
||||||
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 0
|
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 0
|
||||||
%5 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
%5 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
||||||
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 1
|
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 1
|
||||||
@@ -175,17 +192,18 @@ _llgo_0:
|
|||||||
store i32 0, ptr %7, align 4
|
store i32 0, ptr %7, align 4
|
||||||
store i64 0, ptr %9, align 4
|
store i64 0, ptr %9, align 4
|
||||||
%10 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
%10 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 0
|
%11 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %10, i64 16)
|
||||||
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 1
|
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %11, i32 0, i32 0
|
||||||
store ptr %3, ptr %11, align 8
|
%13 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %11, i32 0, i32 1
|
||||||
store ptr %1, ptr %12, align 8
|
store ptr %3, ptr %12, align 8
|
||||||
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, align 8
|
store ptr %1, ptr %13, align 8
|
||||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
|
%14 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %11, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.iface" %14
|
||||||
}
|
}
|
||||||
|
|
||||||
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeInterface"(ptr %0, ptr %1, ptr %2) {
|
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeInterface"(ptr %0, ptr %1, ptr %2) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 0
|
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 0
|
||||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 1
|
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 1
|
||||||
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 2
|
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 2
|
||||||
@@ -196,12 +214,13 @@ _llgo_0:
|
|||||||
store i32 0, ptr %6, align 4
|
store i32 0, ptr %6, align 4
|
||||||
store i64 0, ptr %8, align 4
|
store i64 0, ptr %8, align 4
|
||||||
%9 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
%9 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 0
|
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %9, i64 16)
|
||||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 1
|
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 0
|
||||||
store ptr %3, ptr %10, align 8
|
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 1
|
||||||
store ptr %2, ptr %11, align 8
|
store ptr %3, ptr %11, align 8
|
||||||
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, align 8
|
store ptr %2, ptr %12, align 8
|
||||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
|
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
|
||||||
}
|
}
|
||||||
|
|
||||||
define ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"() {
|
define ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"() {
|
||||||
@@ -213,109 +232,274 @@ _llgo_0:
|
|||||||
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 %1, i64 %2) {
|
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 %1, i64 %2) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%3 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
%3 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
||||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 0
|
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %3, i64 24)
|
||||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 1
|
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %4, i32 0, i32 0
|
||||||
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 2
|
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %4, i32 0, i32 1
|
||||||
store ptr %0, ptr %4, align 8
|
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %4, i32 0, i32 2
|
||||||
store i64 %1, ptr %5, align 4
|
store ptr %0, ptr %5, align 8
|
||||||
store i64 %2, ptr %6, align 4
|
store i64 %1, ptr %6, align 4
|
||||||
%7 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, align 8
|
store i64 %2, ptr %7, align 4
|
||||||
ret %"github.com/goplus/llgo/internal/runtime.Slice" %7
|
%8 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %4, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.Slice" %8
|
||||||
|
}
|
||||||
|
|
||||||
|
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %0, i64 %1, i64 %2, i64 %3, i64 %4, i64 %5) {
|
||||||
|
_llgo_0:
|
||||||
|
%6 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
||||||
|
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %6, i64 24)
|
||||||
|
%8 = icmp slt i64 %3, 0
|
||||||
|
br i1 %8, label %_llgo_1, label %_llgo_5
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_5, %_llgo_4, %_llgo_3, %_llgo_0
|
||||||
|
%9 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 25)
|
||||||
|
%10 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %9)
|
||||||
|
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %10)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
_llgo_2: ; preds = %_llgo_3
|
||||||
|
%11 = sub i64 %4, %3
|
||||||
|
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, i32 0, i32 1
|
||||||
|
store i64 %11, ptr %12, align 4
|
||||||
|
%13 = sub i64 %5, %3
|
||||||
|
%14 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, i32 0, i32 2
|
||||||
|
store i64 %13, ptr %14, align 4
|
||||||
|
%15 = sub i64 %5, %3
|
||||||
|
%16 = icmp sgt i64 %15, 0
|
||||||
|
br i1 %16, label %_llgo_6, label %_llgo_8
|
||||||
|
|
||||||
|
_llgo_3: ; preds = %_llgo_4
|
||||||
|
%17 = icmp sgt i64 %5, %2
|
||||||
|
br i1 %17, label %_llgo_1, label %_llgo_2
|
||||||
|
|
||||||
|
_llgo_4: ; preds = %_llgo_5
|
||||||
|
%18 = icmp slt i64 %5, %4
|
||||||
|
br i1 %18, label %_llgo_1, label %_llgo_3
|
||||||
|
|
||||||
|
_llgo_5: ; preds = %_llgo_0
|
||||||
|
%19 = icmp slt i64 %4, %3
|
||||||
|
br i1 %19, label %_llgo_1, label %_llgo_4
|
||||||
|
|
||||||
|
_llgo_6: ; preds = %_llgo_2
|
||||||
|
%20 = mul i64 %3, %1
|
||||||
|
%21 = getelementptr i8, ptr %0, i64 %20
|
||||||
|
%22 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, i32 0, i32 0
|
||||||
|
store ptr %21, ptr %22, align 8
|
||||||
|
br label %_llgo_7
|
||||||
|
|
||||||
|
_llgo_7: ; preds = %_llgo_8, %_llgo_6
|
||||||
|
%23 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.Slice" %23
|
||||||
|
|
||||||
|
_llgo_8: ; preds = %_llgo_2
|
||||||
|
%24 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, i32 0, i32 0
|
||||||
|
store ptr %0, ptr %24, align 8
|
||||||
|
br label %_llgo_7
|
||||||
}
|
}
|
||||||
|
|
||||||
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr %0, i64 %1) {
|
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr %0, i64 %1) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
%2 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0
|
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
|
||||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1
|
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 0
|
||||||
store ptr %0, ptr %3, align 8
|
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
|
||||||
store i64 %1, ptr %4, align 4
|
store ptr %0, ptr %4, align 8
|
||||||
%5 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %2, align 8
|
store i64 %1, ptr %5, align 4
|
||||||
ret %"github.com/goplus/llgo/internal/runtime.String" %5
|
%6 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %3, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.String" %6
|
||||||
|
}
|
||||||
|
|
||||||
|
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %0, i64 %1, i64 %2) {
|
||||||
|
_llgo_0:
|
||||||
|
%3 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
|
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %3, i64 16)
|
||||||
|
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %4, align 8
|
||||||
|
%5 = icmp slt i64 %1, 0
|
||||||
|
br i1 %5, label %_llgo_1, label %_llgo_4
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_4, %_llgo_3, %_llgo_0
|
||||||
|
%6 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 32)
|
||||||
|
%7 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %6)
|
||||||
|
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %7)
|
||||||
|
unreachable
|
||||||
|
|
||||||
|
_llgo_2: ; preds = %_llgo_3
|
||||||
|
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 1
|
||||||
|
%9 = load i64, ptr %8, align 4
|
||||||
|
%10 = icmp slt i64 %1, %9
|
||||||
|
br i1 %10, label %_llgo_5, label %_llgo_6
|
||||||
|
|
||||||
|
_llgo_3: ; preds = %_llgo_4
|
||||||
|
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 1
|
||||||
|
%12 = load i64, ptr %11, align 4
|
||||||
|
%13 = icmp sgt i64 %2, %12
|
||||||
|
br i1 %13, label %_llgo_1, label %_llgo_2
|
||||||
|
|
||||||
|
_llgo_4: ; preds = %_llgo_0
|
||||||
|
%14 = icmp slt i64 %2, %1
|
||||||
|
br i1 %14, label %_llgo_1, label %_llgo_3
|
||||||
|
|
||||||
|
_llgo_5: ; preds = %_llgo_2
|
||||||
|
%15 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
|
%16 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %15, i64 16)
|
||||||
|
%17 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %16, i32 0, i32 0
|
||||||
|
%18 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 0
|
||||||
|
%19 = load ptr, ptr %18, align 8
|
||||||
|
%20 = getelementptr i8, ptr %19, i64 %1
|
||||||
|
%21 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %16, i32 0, i32 1
|
||||||
|
%22 = sub i64 %2, %1
|
||||||
|
store ptr %20, ptr %17, align 8
|
||||||
|
store i64 %22, ptr %21, align 4
|
||||||
|
%23 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %16, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.String" %23
|
||||||
|
|
||||||
|
_llgo_6: ; preds = %_llgo_2
|
||||||
|
%24 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
|
%25 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %24, i64 16)
|
||||||
|
%26 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %25, i32 0, i32 0
|
||||||
|
%27 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %25, i32 0, i32 1
|
||||||
|
store ptr null, ptr %26, align 8
|
||||||
|
store i64 0, ptr %27, align 4
|
||||||
|
%28 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %25, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.String" %28
|
||||||
}
|
}
|
||||||
|
|
||||||
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NilSlice"() {
|
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NilSlice"() {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%0 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
%0 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
||||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, i32 0, i32 0
|
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 24)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, i32 0, i32 1
|
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 0
|
||||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, i32 0, i32 2
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 1
|
||||||
store ptr null, ptr %1, align 8
|
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 2
|
||||||
store i64 0, ptr %2, align 4
|
store ptr null, ptr %2, align 8
|
||||||
store i64 0, ptr %3, align 4
|
store i64 0, ptr %3, align 4
|
||||||
%4 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, align 8
|
store i64 0, ptr %4, align 4
|
||||||
ret %"github.com/goplus/llgo/internal/runtime.Slice" %4
|
%5 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.Slice" %5
|
||||||
|
}
|
||||||
|
|
||||||
|
define i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
|
||||||
|
_llgo_0:
|
||||||
|
%1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
||||||
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 24)
|
||||||
|
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %2, align 8
|
||||||
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %2, i32 0, i32 2
|
||||||
|
%4 = load i64, ptr %3, align 4
|
||||||
|
ret i64 %4
|
||||||
}
|
}
|
||||||
|
|
||||||
define ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
|
define ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
%1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
||||||
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %1, align 8
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 24)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 0
|
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %2, align 8
|
||||||
%3 = load ptr, ptr %2, align 8
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %2, i32 0, i32 0
|
||||||
ret ptr %3
|
%4 = load ptr, ptr %3, align 8
|
||||||
|
ret ptr %4
|
||||||
}
|
}
|
||||||
|
|
||||||
define i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
|
define i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
%1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
||||||
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %1, align 8
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 24)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 1
|
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %2, align 8
|
||||||
%3 = load i64, ptr %2, align 4
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %2, i32 0, i32 1
|
||||||
ret i64 %3
|
%4 = load i64, ptr %3, align 4
|
||||||
|
ret i64 %4
|
||||||
|
}
|
||||||
|
|
||||||
|
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String" %0, %"github.com/goplus/llgo/internal/runtime.String" %1) {
|
||||||
|
_llgo_0:
|
||||||
|
%2 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
|
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
|
||||||
|
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %3, align 8
|
||||||
|
%4 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
|
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %4, i64 16)
|
||||||
|
store %"github.com/goplus/llgo/internal/runtime.String" %1, ptr %5, align 8
|
||||||
|
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
|
||||||
|
%7 = load i64, ptr %6, align 4
|
||||||
|
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %5, i32 0, i32 1
|
||||||
|
%9 = load i64, ptr %8, align 4
|
||||||
|
%10 = add i64 %7, %9
|
||||||
|
%11 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 %10)
|
||||||
|
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 0
|
||||||
|
%13 = load ptr, ptr %12, align 8
|
||||||
|
%14 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
|
||||||
|
%15 = load i64, ptr %14, align 4
|
||||||
|
%16 = call ptr @memcpy(ptr %11, ptr %13, i64 %15)
|
||||||
|
%17 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
|
||||||
|
%18 = load i64, ptr %17, align 4
|
||||||
|
%19 = getelementptr i8, ptr %11, i64 %18
|
||||||
|
%20 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %5, i32 0, i32 0
|
||||||
|
%21 = load ptr, ptr %20, align 8
|
||||||
|
%22 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %5, i32 0, i32 1
|
||||||
|
%23 = load i64, ptr %22, align 4
|
||||||
|
%24 = call ptr @memcpy(ptr %19, ptr %21, i64 %23)
|
||||||
|
%25 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
|
%26 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %25, i64 16)
|
||||||
|
%27 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %26, i32 0, i32 0
|
||||||
|
%28 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %26, i32 0, i32 1
|
||||||
|
store ptr %11, ptr %27, align 8
|
||||||
|
store i64 %10, ptr %28, align 4
|
||||||
|
%29 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %26, align 8
|
||||||
|
ret %"github.com/goplus/llgo/internal/runtime.String" %29
|
||||||
}
|
}
|
||||||
|
|
||||||
define ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
define ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
%1 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %1, align 8
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 16)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 0
|
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %2, align 8
|
||||||
%3 = load ptr, ptr %2, align 8
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0
|
||||||
ret ptr %3
|
%4 = load ptr, ptr %3, align 8
|
||||||
|
ret ptr %4
|
||||||
}
|
}
|
||||||
|
|
||||||
define i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
|
define i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
%1 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %1, align 8
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 16)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 1
|
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %2, align 8
|
||||||
%3 = load i64, ptr %2, align 4
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1
|
||||||
ret i64 %3
|
%4 = load i64, ptr %3, align 4
|
||||||
|
ret i64 %4
|
||||||
}
|
}
|
||||||
|
|
||||||
define void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %0) {
|
define void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
%1 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||||
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1, align 8
|
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 16)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %1, i32 0, i32 0
|
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %2, align 8
|
||||||
%3 = load ptr, ptr %2, align 8
|
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
|
||||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 1
|
%4 = load ptr, ptr %3, align 8
|
||||||
%5 = load ptr, ptr %4, align 8
|
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
|
||||||
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %5, i32 0, i32 6
|
%6 = load ptr, ptr %5, align 8
|
||||||
%7 = load i8, ptr %6, align 1
|
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %6, i32 0, i32 6
|
||||||
%8 = sext i8 %7 to i64
|
%8 = load i8, ptr %7, align 1
|
||||||
%9 = icmp eq i64 %8, 24
|
%9 = sext i8 %8 to i64
|
||||||
br i1 %9, label %_llgo_2, label %_llgo_1
|
%10 = icmp eq i64 %9, 24
|
||||||
|
br i1 %10, label %_llgo_2, label %_llgo_1
|
||||||
|
|
||||||
_llgo_1: ; preds = %_llgo_2, %_llgo_0
|
_llgo_1: ; preds = %_llgo_2, %_llgo_0
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
_llgo_2: ; preds = %_llgo_0
|
_llgo_2: ; preds = %_llgo_0
|
||||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %1, i32 0, i32 1
|
%11 = load ptr, ptr @__stderrp, align 8
|
||||||
%11 = load ptr, ptr %10, align 8
|
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
|
||||||
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %11, i32 0, i32 1
|
%13 = load ptr, ptr %12, align 8
|
||||||
%13 = load i64, ptr %12, align 4
|
%14 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %13, align 8
|
||||||
%14 = add i64 %13, 1
|
call void @"github.com/goplus/llgo/internal/runtime.stringTracef"(ptr %11, ptr @3, %"github.com/goplus/llgo/internal/runtime.String" %14)
|
||||||
%15 = alloca i8, i64 %14, align 1
|
|
||||||
%16 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %11, align 8
|
|
||||||
%17 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %15, %"github.com/goplus/llgo/internal/runtime.String" %16)
|
|
||||||
%18 = call i32 (ptr, ...) @printf(ptr @1, ptr %17)
|
|
||||||
br label %_llgo_1
|
br label %_llgo_1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 %1) {
|
||||||
|
_llgo_0:
|
||||||
|
%2 = call ptr @memset(ptr %0, i32 0, i64 %1)
|
||||||
|
ret ptr %2
|
||||||
|
}
|
||||||
|
|
||||||
define ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 %0) {
|
define ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 %0) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 48)
|
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 56)
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 0
|
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 0
|
||||||
%3 = getelementptr inbounds i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 %0
|
%3 = getelementptr inbounds i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 %0
|
||||||
%4 = load i64, ptr %3, align 4
|
%4 = load i64, ptr %3, align 4
|
||||||
@@ -329,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
|
||||||
@@ -339,7 +521,7 @@ _llgo_0:
|
|||||||
_llgo_1: ; preds = %_llgo_0
|
_llgo_1: ; preds = %_llgo_0
|
||||||
store i1 true, ptr @"github.com/goplus/llgo/internal/runtime.init$guard", align 1
|
store i1 true, ptr @"github.com/goplus/llgo/internal/runtime.init$guard", align 1
|
||||||
call void @"github.com/goplus/llgo/internal/abi.init"()
|
call void @"github.com/goplus/llgo/internal/abi.init"()
|
||||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 80)
|
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 88)
|
||||||
store ptr %1, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
store ptr %1, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
||||||
store i64 1, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 1), align 4
|
store i64 1, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 1), align 4
|
||||||
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 2), align 4
|
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 2), align 4
|
||||||
@@ -406,17 +588,36 @@ _llgo_0:
|
|||||||
|
|
||||||
define ptr @"github.com/goplus/llgo/internal/runtime.makemap_small"() {
|
define ptr @"github.com/goplus/llgo/internal/runtime.makemap_small"() {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 48)
|
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 48)
|
||||||
%1 = call i32 @rand()
|
%1 = call i32 @rand()
|
||||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.hmap", ptr %0, i32 0, i32 4
|
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.hmap", ptr %0, i32 0, i32 4
|
||||||
store i32 %1, ptr %2, align 4
|
store i32 %1, ptr %2, align 4
|
||||||
ret ptr %0
|
ret ptr %0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define void @"github.com/goplus/llgo/internal/runtime.stringTracef"(ptr %0, ptr %1, %"github.com/goplus/llgo/internal/runtime.String" %2) {
|
||||||
|
_llgo_0:
|
||||||
|
%3 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||||
|
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %3, i64 16)
|
||||||
|
store %"github.com/goplus/llgo/internal/runtime.String" %2, ptr %4, align 8
|
||||||
|
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 1
|
||||||
|
%6 = load i64, ptr %5, align 4
|
||||||
|
%7 = add i64 %6, 1
|
||||||
|
%8 = alloca i8, i64 %7, align 1
|
||||||
|
%9 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %4, align 8
|
||||||
|
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %8, %"github.com/goplus/llgo/internal/runtime.String" %9)
|
||||||
|
%11 = call i32 (ptr, ptr, ...) @fprintf(ptr %0, ptr %1, ptr %10)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
declare ptr @malloc(i64)
|
declare ptr @malloc(i64)
|
||||||
|
|
||||||
|
declare ptr @memset(ptr, i32, i64)
|
||||||
|
|
||||||
declare ptr @memcpy(ptr, ptr, i64)
|
declare ptr @memcpy(ptr, ptr, i64)
|
||||||
|
|
||||||
declare i32 @printf(ptr, ...)
|
|
||||||
|
|
||||||
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, ...)
|
||||||
|
|||||||
@@ -23,19 +23,33 @@ import (
|
|||||||
"github.com/goplus/llgo/internal/runtime/c"
|
"github.com/goplus/llgo/internal/runtime/c"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Alloc allocates memory.
|
// AllocU allocates uninitialized memory.
|
||||||
func Alloc(size uintptr) unsafe.Pointer {
|
func AllocU(size uintptr) unsafe.Pointer {
|
||||||
return c.Malloc(size)
|
return c.Malloc(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllocZ allocates zero-initialized memory.
|
||||||
|
func AllocZ(size uintptr) unsafe.Pointer {
|
||||||
|
ret := c.Malloc(size)
|
||||||
|
return c.Memset(ret, 0, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zeroinit initializes memory to zero.
|
||||||
|
func Zeroinit(p c.Pointer, size uintptr) c.Pointer {
|
||||||
|
return c.Memset(p, 0, size)
|
||||||
|
}
|
||||||
|
|
||||||
// TracePanic prints panic message.
|
// TracePanic prints panic message.
|
||||||
func TracePanic(v Interface) {
|
func TracePanic(v Interface) {
|
||||||
kind := abi.Kind(v.tab._type.Kind_)
|
kind := abi.Kind(v.tab._type.Kind_)
|
||||||
switch {
|
switch {
|
||||||
case kind == abi.String:
|
case kind == abi.String:
|
||||||
s := (*String)(v.data)
|
stringTracef(c.Stderr, c.Str("panic: %s\n"), *(*String)(v.data))
|
||||||
cs := c.Alloca(uintptr(s.len) + 1)
|
|
||||||
c.Printf(c.Str("panic: %s\n"), CStrCopy(cs, *s))
|
|
||||||
}
|
}
|
||||||
// TODO(xsw): other message type
|
// TODO(xsw): other message type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringTracef(fp c.FilePtr, format *c.Char, s String) {
|
||||||
|
cs := c.Alloca(uintptr(s.len) + 1)
|
||||||
|
c.Fprintf(fp, format, CStrCopy(cs, s))
|
||||||
|
}
|
||||||
|
|||||||
44
internal/runtime/z_closure.go
Normal file
44
internal/runtime/z_closure.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
/*
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Closure represents a closure.
|
||||||
|
type Closure struct {
|
||||||
|
f unsafe.Pointer
|
||||||
|
data unsafe.Pointer // means no context if data is nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClosure creates a closure.
|
||||||
|
func NewClosure(f, data unsafe.Pointer) Closure {
|
||||||
|
return Closure{f, data}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClosureF returns the function of a closure.
|
||||||
|
func ClosureF(c Closure) unsafe.Pointer {
|
||||||
|
return c.f
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClosureData returns the data of a closure.
|
||||||
|
func ClosureData(c Closure) unsafe.Pointer {
|
||||||
|
return c.data
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -18,6 +18,8 @@ package runtime
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/internal/runtime/c"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -39,11 +41,30 @@ func NewSlice(data unsafe.Pointer, len, cap int) Slice {
|
|||||||
return Slice{data, len, cap}
|
return Slice{data, len, cap}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSlice3(base unsafe.Pointer, eltSize, cap, i, j, k int) (s Slice) {
|
||||||
|
if i < 0 || j < i || k < j || k > cap {
|
||||||
|
panic("slice index out of bounds")
|
||||||
|
}
|
||||||
|
s.len = j - i
|
||||||
|
s.cap = k - i
|
||||||
|
if k-i > 0 {
|
||||||
|
s.data = c.Advance(base, i*eltSize)
|
||||||
|
} else {
|
||||||
|
s.data = base
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// SliceLen returns the length of a slice.
|
// SliceLen returns the length of a slice.
|
||||||
func SliceLen(s Slice) int {
|
func SliceLen(s Slice) int {
|
||||||
return s.len
|
return s.len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SliceCap returns the capacity of a slice.
|
||||||
|
func SliceCap(s Slice) int {
|
||||||
|
return s.cap
|
||||||
|
}
|
||||||
|
|
||||||
// SliceData returns the data pointer of a slice.
|
// SliceData returns the data pointer of a slice.
|
||||||
func SliceData(s Slice) unsafe.Pointer {
|
func SliceData(s Slice) unsafe.Pointer {
|
||||||
return s.data
|
return s.data
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func NewString(data unsafe.Pointer, len int) String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StringLen returns the length of a string.
|
// StringLen returns the length of a string.
|
||||||
func StringLen(s Slice) int {
|
func StringLen(s String) int {
|
||||||
return s.len
|
return s.len
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +55,15 @@ func StringData(s String) unsafe.Pointer {
|
|||||||
return s.data
|
return s.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringCat concatenates two strings.
|
||||||
|
func StringCat(a, b String) String {
|
||||||
|
n := a.len + b.len
|
||||||
|
dest := AllocU(uintptr(n))
|
||||||
|
c.Memcpy(dest, a.data, uintptr(a.len))
|
||||||
|
c.Memcpy(c.Advance(dest, a.len), b.data, uintptr(b.len))
|
||||||
|
return String{dest, n}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// CStrCopy copies a Go string to a C string buffer and returns it.
|
// CStrCopy copies a Go string to a C string buffer and returns it.
|
||||||
@@ -67,8 +76,18 @@ func CStrCopy(dest unsafe.Pointer, s String) *int8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CStrDup(s String) *int8 {
|
func CStrDup(s String) *int8 {
|
||||||
dest := Alloc(uintptr(s.len + 1))
|
dest := AllocU(uintptr(s.len + 1))
|
||||||
return CStrCopy(dest, s)
|
return CStrCopy(dest, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewStringSlice(base String, i, j int) String {
|
||||||
|
if i < 0 || j < i || j > base.len {
|
||||||
|
panic("string slice index out of bounds")
|
||||||
|
}
|
||||||
|
if i < base.len {
|
||||||
|
return String{c.Advance(base.data, i), j - i}
|
||||||
|
}
|
||||||
|
return String{nil, 0}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
218
internal/typeparams/normalize.go
Normal file
218
internal/typeparams/normalize.go
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package typeparams
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/types"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run copytermlist.go
|
||||||
|
|
||||||
|
const debug = false
|
||||||
|
|
||||||
|
var ErrEmptyTypeSet = errors.New("empty type set")
|
||||||
|
|
||||||
|
// StructuralTerms returns a slice of terms representing the normalized
|
||||||
|
// structural type restrictions of a type parameter, if any.
|
||||||
|
//
|
||||||
|
// Structural type restrictions of a type parameter are created via
|
||||||
|
// non-interface types embedded in its constraint interface (directly, or via a
|
||||||
|
// chain of interface embeddings). For example, in the declaration
|
||||||
|
//
|
||||||
|
// type T[P interface{~int; m()}] int
|
||||||
|
//
|
||||||
|
// the structural restriction of the type parameter P is ~int.
|
||||||
|
//
|
||||||
|
// With interface embedding and unions, the specification of structural type
|
||||||
|
// restrictions may be arbitrarily complex. For example, consider the
|
||||||
|
// following:
|
||||||
|
//
|
||||||
|
// type A interface{ ~string|~[]byte }
|
||||||
|
//
|
||||||
|
// type B interface{ int|string }
|
||||||
|
//
|
||||||
|
// type C interface { ~string|~int }
|
||||||
|
//
|
||||||
|
// type T[P interface{ A|B; C }] int
|
||||||
|
//
|
||||||
|
// In this example, the structural type restriction of P is ~string|int: A|B
|
||||||
|
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
|
||||||
|
// which when intersected with C (~string|~int) yields ~string|int.
|
||||||
|
//
|
||||||
|
// StructuralTerms computes these expansions and reductions, producing a
|
||||||
|
// "normalized" form of the embeddings. A structural restriction is normalized
|
||||||
|
// if it is a single union containing no interface terms, and is minimal in the
|
||||||
|
// sense that removing any term changes the set of types satisfying the
|
||||||
|
// constraint. It is left as a proof for the reader that, modulo sorting, there
|
||||||
|
// is exactly one such normalized form.
|
||||||
|
//
|
||||||
|
// Because the minimal representation always takes this form, StructuralTerms
|
||||||
|
// returns a slice of tilde terms corresponding to the terms of the union in
|
||||||
|
// the normalized structural restriction. An error is returned if the
|
||||||
|
// constraint interface is invalid, exceeds complexity bounds, or has an empty
|
||||||
|
// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet.
|
||||||
|
//
|
||||||
|
// StructuralTerms makes no guarantees about the order of terms, except that it
|
||||||
|
// is deterministic.
|
||||||
|
func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) {
|
||||||
|
constraint := tparam.Constraint()
|
||||||
|
if constraint == nil {
|
||||||
|
return nil, fmt.Errorf("%s has nil constraint", tparam)
|
||||||
|
}
|
||||||
|
iface, _ := constraint.Underlying().(*types.Interface)
|
||||||
|
if iface == nil {
|
||||||
|
return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying())
|
||||||
|
}
|
||||||
|
return InterfaceTermSet(iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceTermSet computes the normalized terms for a constraint interface,
|
||||||
|
// returning an error if the term set cannot be computed or is empty. In the
|
||||||
|
// latter case, the error will be ErrEmptyTypeSet.
|
||||||
|
//
|
||||||
|
// See the documentation of StructuralTerms for more information on
|
||||||
|
// normalization.
|
||||||
|
func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) {
|
||||||
|
return computeTermSet(iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnionTermSet computes the normalized terms for a union, returning an error
|
||||||
|
// if the term set cannot be computed or is empty. In the latter case, the
|
||||||
|
// error will be ErrEmptyTypeSet.
|
||||||
|
//
|
||||||
|
// See the documentation of StructuralTerms for more information on
|
||||||
|
// normalization.
|
||||||
|
func UnionTermSet(union *types.Union) ([]*types.Term, error) {
|
||||||
|
return computeTermSet(union)
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeTermSet(typ types.Type) ([]*types.Term, error) {
|
||||||
|
tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tset.terms.isEmpty() {
|
||||||
|
return nil, ErrEmptyTypeSet
|
||||||
|
}
|
||||||
|
if tset.terms.isAll() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var terms []*types.Term
|
||||||
|
for _, term := range tset.terms {
|
||||||
|
terms = append(terms, types.NewTerm(term.tilde, term.typ))
|
||||||
|
}
|
||||||
|
return terms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A termSet holds the normalized set of terms for a given type.
|
||||||
|
//
|
||||||
|
// The name termSet is intentionally distinct from 'type set': a type set is
|
||||||
|
// all types that implement a type (and includes method restrictions), whereas
|
||||||
|
// a term set just represents the structural restrictions on a type.
|
||||||
|
type termSet struct {
|
||||||
|
complete bool
|
||||||
|
terms termlist
|
||||||
|
}
|
||||||
|
|
||||||
|
func indentf(depth int, format string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
|
||||||
|
if t == nil {
|
||||||
|
panic("nil type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
indentf(depth, "%s", t.String())
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
indentf(depth, "=> %s", err)
|
||||||
|
} else {
|
||||||
|
indentf(depth, "=> %s", res.terms.String())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxTermCount = 100
|
||||||
|
if tset, ok := seen[t]; ok {
|
||||||
|
if !tset.complete {
|
||||||
|
return nil, fmt.Errorf("cycle detected in the declaration of %s", t)
|
||||||
|
}
|
||||||
|
return tset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the current type as seen to avoid infinite recursion.
|
||||||
|
tset := new(termSet)
|
||||||
|
defer func() {
|
||||||
|
tset.complete = true
|
||||||
|
}()
|
||||||
|
seen[t] = tset
|
||||||
|
|
||||||
|
switch u := t.Underlying().(type) {
|
||||||
|
case *types.Interface:
|
||||||
|
// The term set of an interface is the intersection of the term sets of its
|
||||||
|
// embedded types.
|
||||||
|
tset.terms = allTermlist
|
||||||
|
for i := 0; i < u.NumEmbeddeds(); i++ {
|
||||||
|
embedded := u.EmbeddedType(i)
|
||||||
|
if _, ok := embedded.Underlying().(*types.TypeParam); ok {
|
||||||
|
return nil, fmt.Errorf("invalid embedded type %T", embedded)
|
||||||
|
}
|
||||||
|
tset2, err := computeTermSetInternal(embedded, seen, depth+1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tset.terms = tset.terms.intersect(tset2.terms)
|
||||||
|
}
|
||||||
|
case *types.Union:
|
||||||
|
// The term set of a union is the union of term sets of its terms.
|
||||||
|
tset.terms = nil
|
||||||
|
for i := 0; i < u.Len(); i++ {
|
||||||
|
t := u.Term(i)
|
||||||
|
var terms termlist
|
||||||
|
switch t.Type().Underlying().(type) {
|
||||||
|
case *types.Interface:
|
||||||
|
tset2, err := computeTermSetInternal(t.Type(), seen, depth+1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
terms = tset2.terms
|
||||||
|
case *types.TypeParam, *types.Union:
|
||||||
|
// A stand-alone type parameter or union is not permitted as union
|
||||||
|
// term.
|
||||||
|
return nil, fmt.Errorf("invalid union term %T", t)
|
||||||
|
default:
|
||||||
|
if t.Type() == types.Typ[types.Invalid] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
terms = termlist{{t.Tilde(), t.Type()}}
|
||||||
|
}
|
||||||
|
tset.terms = tset.terms.union(terms)
|
||||||
|
if len(tset.terms) > maxTermCount {
|
||||||
|
return nil, fmt.Errorf("exceeded max term count %d", maxTermCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *types.TypeParam:
|
||||||
|
panic("unreachable")
|
||||||
|
default:
|
||||||
|
// For all other types, the term set is just a single non-tilde term
|
||||||
|
// holding the type itself.
|
||||||
|
if u != types.Typ[types.Invalid] {
|
||||||
|
tset.terms = termlist{{false, t}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// under is a facade for the go/types internal function of the same name. It is
|
||||||
|
// used by typeterm.go.
|
||||||
|
func under(t types.Type) types.Type {
|
||||||
|
return t.Underlying()
|
||||||
|
}
|
||||||
163
internal/typeparams/termlist.go
Normal file
163
internal/typeparams/termlist.go
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Code generated by copytermlist.go DO NOT EDIT.
|
||||||
|
|
||||||
|
package typeparams
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"go/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A termlist represents the type set represented by the union
|
||||||
|
// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn.
|
||||||
|
// A termlist is in normal form if all terms are disjoint.
|
||||||
|
// termlist operations don't require the operands to be in
|
||||||
|
// normal form.
|
||||||
|
type termlist []*term
|
||||||
|
|
||||||
|
// allTermlist represents the set of all types.
|
||||||
|
// It is in normal form.
|
||||||
|
var allTermlist = termlist{new(term)}
|
||||||
|
|
||||||
|
// String prints the termlist exactly (without normalization).
|
||||||
|
func (xl termlist) String() string {
|
||||||
|
if len(xl) == 0 {
|
||||||
|
return "∅"
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i, x := range xl {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(" | ")
|
||||||
|
}
|
||||||
|
buf.WriteString(x.String())
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isEmpty reports whether the termlist xl represents the empty set of types.
|
||||||
|
func (xl termlist) isEmpty() bool {
|
||||||
|
// If there's a non-nil term, the entire list is not empty.
|
||||||
|
// If the termlist is in normal form, this requires at most
|
||||||
|
// one iteration.
|
||||||
|
for _, x := range xl {
|
||||||
|
if x != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isAll reports whether the termlist xl represents the set of all types.
|
||||||
|
func (xl termlist) isAll() bool {
|
||||||
|
// If there's a 𝓤 term, the entire list is 𝓤.
|
||||||
|
// If the termlist is in normal form, this requires at most
|
||||||
|
// one iteration.
|
||||||
|
for _, x := range xl {
|
||||||
|
if x != nil && x.typ == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// norm returns the normal form of xl.
|
||||||
|
func (xl termlist) norm() termlist {
|
||||||
|
// Quadratic algorithm, but good enough for now.
|
||||||
|
// TODO(gri) fix asymptotic performance
|
||||||
|
used := make([]bool, len(xl))
|
||||||
|
var rl termlist
|
||||||
|
for i, xi := range xl {
|
||||||
|
if xi == nil || used[i] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for j := i + 1; j < len(xl); j++ {
|
||||||
|
xj := xl[j]
|
||||||
|
if xj == nil || used[j] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if u1, u2 := xi.union(xj); u2 == nil {
|
||||||
|
// If we encounter a 𝓤 term, the entire list is 𝓤.
|
||||||
|
// Exit early.
|
||||||
|
// (Note that this is not just an optimization;
|
||||||
|
// if we continue, we may end up with a 𝓤 term
|
||||||
|
// and other terms and the result would not be
|
||||||
|
// in normal form.)
|
||||||
|
if u1.typ == nil {
|
||||||
|
return allTermlist
|
||||||
|
}
|
||||||
|
xi = u1
|
||||||
|
used[j] = true // xj is now unioned into xi - ignore it in future iterations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rl = append(rl, xi)
|
||||||
|
}
|
||||||
|
return rl
|
||||||
|
}
|
||||||
|
|
||||||
|
// union returns the union xl ∪ yl.
|
||||||
|
func (xl termlist) union(yl termlist) termlist {
|
||||||
|
return append(xl, yl...).norm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// intersect returns the intersection xl ∩ yl.
|
||||||
|
func (xl termlist) intersect(yl termlist) termlist {
|
||||||
|
if xl.isEmpty() || yl.isEmpty() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quadratic algorithm, but good enough for now.
|
||||||
|
// TODO(gri) fix asymptotic performance
|
||||||
|
var rl termlist
|
||||||
|
for _, x := range xl {
|
||||||
|
for _, y := range yl {
|
||||||
|
if r := x.intersect(y); r != nil {
|
||||||
|
rl = append(rl, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rl.norm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// equal reports whether xl and yl represent the same type set.
|
||||||
|
func (xl termlist) equal(yl termlist) bool {
|
||||||
|
// TODO(gri) this should be more efficient
|
||||||
|
return xl.subsetOf(yl) && yl.subsetOf(xl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// includes reports whether t ∈ xl.
|
||||||
|
func (xl termlist) includes(t types.Type) bool {
|
||||||
|
for _, x := range xl {
|
||||||
|
if x.includes(t) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// supersetOf reports whether y ⊆ xl.
|
||||||
|
func (xl termlist) supersetOf(y *term) bool {
|
||||||
|
for _, x := range xl {
|
||||||
|
if y.subsetOf(x) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// subsetOf reports whether xl ⊆ yl.
|
||||||
|
func (xl termlist) subsetOf(yl termlist) bool {
|
||||||
|
if yl.isEmpty() {
|
||||||
|
return xl.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// each term x of xl must be a subset of yl
|
||||||
|
for _, x := range xl {
|
||||||
|
if !yl.supersetOf(x) {
|
||||||
|
return false // x is not a subset yl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
169
internal/typeparams/typeterm.go
Normal file
169
internal/typeparams/typeterm.go
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Code generated by copytermlist.go DO NOT EDIT.
|
||||||
|
|
||||||
|
package typeparams
|
||||||
|
|
||||||
|
import "go/types"
|
||||||
|
|
||||||
|
// A term describes elementary type sets:
|
||||||
|
//
|
||||||
|
// ∅: (*term)(nil) == ∅ // set of no types (empty set)
|
||||||
|
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
|
||||||
|
// T: &term{false, T} == {T} // set of type T
|
||||||
|
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
|
||||||
|
type term struct {
|
||||||
|
tilde bool // valid if typ != nil
|
||||||
|
typ types.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *term) String() string {
|
||||||
|
switch {
|
||||||
|
case x == nil:
|
||||||
|
return "∅"
|
||||||
|
case x.typ == nil:
|
||||||
|
return "𝓤"
|
||||||
|
case x.tilde:
|
||||||
|
return "~" + x.typ.String()
|
||||||
|
default:
|
||||||
|
return x.typ.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// equal reports whether x and y represent the same type set.
|
||||||
|
func (x *term) equal(y *term) bool {
|
||||||
|
// easy cases
|
||||||
|
switch {
|
||||||
|
case x == nil || y == nil:
|
||||||
|
return x == y
|
||||||
|
case x.typ == nil || y.typ == nil:
|
||||||
|
return x.typ == y.typ
|
||||||
|
}
|
||||||
|
// ∅ ⊂ x, y ⊂ 𝓤
|
||||||
|
|
||||||
|
return x.tilde == y.tilde && types.Identical(x.typ, y.typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// union returns the union x ∪ y: zero, one, or two non-nil terms.
|
||||||
|
func (x *term) union(y *term) (_, _ *term) {
|
||||||
|
// easy cases
|
||||||
|
switch {
|
||||||
|
case x == nil && y == nil:
|
||||||
|
return nil, nil // ∅ ∪ ∅ == ∅
|
||||||
|
case x == nil:
|
||||||
|
return y, nil // ∅ ∪ y == y
|
||||||
|
case y == nil:
|
||||||
|
return x, nil // x ∪ ∅ == x
|
||||||
|
case x.typ == nil:
|
||||||
|
return x, nil // 𝓤 ∪ y == 𝓤
|
||||||
|
case y.typ == nil:
|
||||||
|
return y, nil // x ∪ 𝓤 == 𝓤
|
||||||
|
}
|
||||||
|
// ∅ ⊂ x, y ⊂ 𝓤
|
||||||
|
|
||||||
|
if x.disjoint(y) {
|
||||||
|
return x, y // x ∪ y == (x, y) if x ∩ y == ∅
|
||||||
|
}
|
||||||
|
// x.typ == y.typ
|
||||||
|
|
||||||
|
// ~t ∪ ~t == ~t
|
||||||
|
// ~t ∪ T == ~t
|
||||||
|
// T ∪ ~t == ~t
|
||||||
|
// T ∪ T == T
|
||||||
|
if x.tilde || !y.tilde {
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
return y, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// intersect returns the intersection x ∩ y.
|
||||||
|
func (x *term) intersect(y *term) *term {
|
||||||
|
// easy cases
|
||||||
|
switch {
|
||||||
|
case x == nil || y == nil:
|
||||||
|
return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅
|
||||||
|
case x.typ == nil:
|
||||||
|
return y // 𝓤 ∩ y == y
|
||||||
|
case y.typ == nil:
|
||||||
|
return x // x ∩ 𝓤 == x
|
||||||
|
}
|
||||||
|
// ∅ ⊂ x, y ⊂ 𝓤
|
||||||
|
|
||||||
|
if x.disjoint(y) {
|
||||||
|
return nil // x ∩ y == ∅ if x ∩ y == ∅
|
||||||
|
}
|
||||||
|
// x.typ == y.typ
|
||||||
|
|
||||||
|
// ~t ∩ ~t == ~t
|
||||||
|
// ~t ∩ T == T
|
||||||
|
// T ∩ ~t == T
|
||||||
|
// T ∩ T == T
|
||||||
|
if !x.tilde || y.tilde {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
// includes reports whether t ∈ x.
|
||||||
|
func (x *term) includes(t types.Type) bool {
|
||||||
|
// easy cases
|
||||||
|
switch {
|
||||||
|
case x == nil:
|
||||||
|
return false // t ∈ ∅ == false
|
||||||
|
case x.typ == nil:
|
||||||
|
return true // t ∈ 𝓤 == true
|
||||||
|
}
|
||||||
|
// ∅ ⊂ x ⊂ 𝓤
|
||||||
|
|
||||||
|
u := t
|
||||||
|
if x.tilde {
|
||||||
|
u = under(u)
|
||||||
|
}
|
||||||
|
return types.Identical(x.typ, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
// subsetOf reports whether x ⊆ y.
|
||||||
|
func (x *term) subsetOf(y *term) bool {
|
||||||
|
// easy cases
|
||||||
|
switch {
|
||||||
|
case x == nil:
|
||||||
|
return true // ∅ ⊆ y == true
|
||||||
|
case y == nil:
|
||||||
|
return false // x ⊆ ∅ == false since x != ∅
|
||||||
|
case y.typ == nil:
|
||||||
|
return true // x ⊆ 𝓤 == true
|
||||||
|
case x.typ == nil:
|
||||||
|
return false // 𝓤 ⊆ y == false since y != 𝓤
|
||||||
|
}
|
||||||
|
// ∅ ⊂ x, y ⊂ 𝓤
|
||||||
|
|
||||||
|
if x.disjoint(y) {
|
||||||
|
return false // x ⊆ y == false if x ∩ y == ∅
|
||||||
|
}
|
||||||
|
// x.typ == y.typ
|
||||||
|
|
||||||
|
// ~t ⊆ ~t == true
|
||||||
|
// ~t ⊆ T == false
|
||||||
|
// T ⊆ ~t == true
|
||||||
|
// T ⊆ T == true
|
||||||
|
return !x.tilde || y.tilde
|
||||||
|
}
|
||||||
|
|
||||||
|
// disjoint reports whether x ∩ y == ∅.
|
||||||
|
// x.typ and y.typ must not be nil.
|
||||||
|
func (x *term) disjoint(y *term) bool {
|
||||||
|
if debug && (x.typ == nil || y.typ == nil) {
|
||||||
|
panic("invalid argument(s)")
|
||||||
|
}
|
||||||
|
ux := x.typ
|
||||||
|
if y.tilde {
|
||||||
|
ux = under(ux)
|
||||||
|
}
|
||||||
|
uy := y.typ
|
||||||
|
if x.tilde {
|
||||||
|
uy = under(uy)
|
||||||
|
}
|
||||||
|
return !types.Identical(ux, uy)
|
||||||
|
}
|
||||||
525
internal/typeutil/map.go
Normal file
525
internal/typeutil/map.go
Normal file
@@ -0,0 +1,525 @@
|
|||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package typeutil defines various utilities for types, such as Map,
|
||||||
|
// a mapping from types.Type to interface{} values.
|
||||||
|
package typeutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/types"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/internal/aliases"
|
||||||
|
"github.com/goplus/llgo/internal/typeparams"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Map is a hash-table-based mapping from types (types.Type) to
|
||||||
|
// arbitrary interface{} values. The concrete types that implement
|
||||||
|
// the Type interface are pointers. Since they are not canonicalized,
|
||||||
|
// == cannot be used to check for equivalence, and thus we cannot
|
||||||
|
// simply use a Go map.
|
||||||
|
//
|
||||||
|
// Just as with map[K]V, a nil *Map is a valid empty map.
|
||||||
|
//
|
||||||
|
// Not thread-safe.
|
||||||
|
type Map struct {
|
||||||
|
hasher Hasher // shared by many Maps
|
||||||
|
table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
|
||||||
|
length int // number of map entries
|
||||||
|
}
|
||||||
|
|
||||||
|
// entry is an entry (key/value association) in a hash bucket.
|
||||||
|
type entry struct {
|
||||||
|
key types.Type
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHasher sets the hasher used by Map.
|
||||||
|
//
|
||||||
|
// All Hashers are functionally equivalent but contain internal state
|
||||||
|
// used to cache the results of hashing previously seen types.
|
||||||
|
//
|
||||||
|
// A single Hasher created by MakeHasher() may be shared among many
|
||||||
|
// Maps. This is recommended if the instances have many keys in
|
||||||
|
// common, as it will amortize the cost of hash computation.
|
||||||
|
//
|
||||||
|
// A Hasher may grow without bound as new types are seen. Even when a
|
||||||
|
// type is deleted from the map, the Hasher never shrinks, since other
|
||||||
|
// types in the map may reference the deleted type indirectly.
|
||||||
|
//
|
||||||
|
// Hashers are not thread-safe, and read-only operations such as
|
||||||
|
// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
|
||||||
|
// read-lock) is require around all Map operations if a shared
|
||||||
|
// hasher is accessed from multiple threads.
|
||||||
|
//
|
||||||
|
// If SetHasher is not called, the Map will create a private hasher at
|
||||||
|
// the first call to Insert.
|
||||||
|
func (m *Map) SetHasher(hasher Hasher) {
|
||||||
|
m.hasher = hasher
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes the entry with the given key, if any.
|
||||||
|
// It returns true if the entry was found.
|
||||||
|
func (m *Map) Delete(key types.Type) bool {
|
||||||
|
if m != nil && m.table != nil {
|
||||||
|
hash := m.hasher.Hash(key)
|
||||||
|
bucket := m.table[hash]
|
||||||
|
for i, e := range bucket {
|
||||||
|
if e.key != nil && types.Identical(key, e.key) {
|
||||||
|
// We can't compact the bucket as it
|
||||||
|
// would disturb iterators.
|
||||||
|
bucket[i] = entry{}
|
||||||
|
m.length--
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// At returns the map entry for the given key.
|
||||||
|
// The result is nil if the entry is not present.
|
||||||
|
func (m *Map) At(key types.Type) interface{} {
|
||||||
|
if m != nil && m.table != nil {
|
||||||
|
for _, e := range m.table[m.hasher.Hash(key)] {
|
||||||
|
if e.key != nil && types.Identical(key, e.key) {
|
||||||
|
return e.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the map entry for key to val,
|
||||||
|
// and returns the previous entry, if any.
|
||||||
|
func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
|
||||||
|
if m.table != nil {
|
||||||
|
hash := m.hasher.Hash(key)
|
||||||
|
bucket := m.table[hash]
|
||||||
|
var hole *entry
|
||||||
|
for i, e := range bucket {
|
||||||
|
if e.key == nil {
|
||||||
|
hole = &bucket[i]
|
||||||
|
} else if types.Identical(key, e.key) {
|
||||||
|
prev = e.value
|
||||||
|
bucket[i].value = value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hole != nil {
|
||||||
|
*hole = entry{key, value} // overwrite deleted entry
|
||||||
|
} else {
|
||||||
|
m.table[hash] = append(bucket, entry{key, value})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if m.hasher.memo == nil {
|
||||||
|
m.hasher = MakeHasher()
|
||||||
|
}
|
||||||
|
hash := m.hasher.Hash(key)
|
||||||
|
m.table = map[uint32][]entry{hash: {entry{key, value}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.length++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of map entries.
|
||||||
|
func (m *Map) Len() int {
|
||||||
|
if m != nil {
|
||||||
|
return m.length
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate calls function f on each entry in the map in unspecified order.
|
||||||
|
//
|
||||||
|
// If f should mutate the map, Iterate provides the same guarantees as
|
||||||
|
// Go maps: if f deletes a map entry that Iterate has not yet reached,
|
||||||
|
// f will not be invoked for it, but if f inserts a map entry that
|
||||||
|
// Iterate has not yet reached, whether or not f will be invoked for
|
||||||
|
// it is unspecified.
|
||||||
|
func (m *Map) Iterate(f func(key types.Type, value interface{})) {
|
||||||
|
if m != nil {
|
||||||
|
for _, bucket := range m.table {
|
||||||
|
for _, e := range bucket {
|
||||||
|
if e.key != nil {
|
||||||
|
f(e.key, e.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a new slice containing the set of map keys.
|
||||||
|
// The order is unspecified.
|
||||||
|
func (m *Map) Keys() []types.Type {
|
||||||
|
keys := make([]types.Type, 0, m.Len())
|
||||||
|
m.Iterate(func(key types.Type, _ interface{}) {
|
||||||
|
keys = append(keys, key)
|
||||||
|
})
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) toString(values bool) string {
|
||||||
|
if m == nil {
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprint(&buf, "{")
|
||||||
|
sep := ""
|
||||||
|
m.Iterate(func(key types.Type, value interface{}) {
|
||||||
|
fmt.Fprint(&buf, sep)
|
||||||
|
sep = ", "
|
||||||
|
fmt.Fprint(&buf, key)
|
||||||
|
if values {
|
||||||
|
fmt.Fprintf(&buf, ": %q", value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fmt.Fprint(&buf, "}")
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the map's entries.
|
||||||
|
// Values are printed using fmt.Sprintf("%v", v).
|
||||||
|
// Order is unspecified.
|
||||||
|
func (m *Map) String() string {
|
||||||
|
return m.toString(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeysString returns a string representation of the map's key set.
|
||||||
|
// Order is unspecified.
|
||||||
|
func (m *Map) KeysString() string {
|
||||||
|
return m.toString(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Hasher
|
||||||
|
|
||||||
|
// A Hasher maps each type to its hash value.
|
||||||
|
// For efficiency, a hasher uses memoization; thus its memory
|
||||||
|
// footprint grows monotonically over time.
|
||||||
|
// Hashers are not thread-safe.
|
||||||
|
// Hashers have reference semantics.
|
||||||
|
// Call MakeHasher to create a Hasher.
|
||||||
|
type Hasher struct {
|
||||||
|
memo map[types.Type]uint32
|
||||||
|
|
||||||
|
// ptrMap records pointer identity.
|
||||||
|
ptrMap map[interface{}]uint32
|
||||||
|
|
||||||
|
// sigTParams holds type parameters from the signature being hashed.
|
||||||
|
// Signatures are considered identical modulo renaming of type parameters, so
|
||||||
|
// within the scope of a signature type the identity of the signature's type
|
||||||
|
// parameters is just their index.
|
||||||
|
//
|
||||||
|
// Since the language does not currently support referring to uninstantiated
|
||||||
|
// generic types or functions, and instantiated signatures do not have type
|
||||||
|
// parameter lists, we should never encounter a second non-empty type
|
||||||
|
// parameter list when hashing a generic signature.
|
||||||
|
sigTParams *types.TypeParamList
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeHasher returns a new Hasher instance.
|
||||||
|
func MakeHasher() Hasher {
|
||||||
|
return Hasher{
|
||||||
|
memo: make(map[types.Type]uint32),
|
||||||
|
ptrMap: make(map[interface{}]uint32),
|
||||||
|
sigTParams: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash computes a hash value for the given type t such that
|
||||||
|
// Identical(t, t') => Hash(t) == Hash(t').
|
||||||
|
func (h Hasher) Hash(t types.Type) uint32 {
|
||||||
|
hash, ok := h.memo[t]
|
||||||
|
if !ok {
|
||||||
|
hash = h.hashFor(t)
|
||||||
|
h.memo[t] = hash
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashString computes the Fowler–Noll–Vo hash of s.
|
||||||
|
func hashString(s string) uint32 {
|
||||||
|
var h uint32
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
h ^= uint32(s[i])
|
||||||
|
h *= 16777619
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func HashSig(h Hasher, t *types.Signature) uint32 {
|
||||||
|
var hash uint32 = 9091
|
||||||
|
if t.Variadic() {
|
||||||
|
hash *= 8863
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a separate hasher for types inside of the signature, where type
|
||||||
|
// parameter identity is modified to be (index, constraint). We must use a
|
||||||
|
// new memo for this hasher as type identity may be affected by this
|
||||||
|
// masking. For example, in func[T any](*T), the identity of *T depends on
|
||||||
|
// whether we are mapping the argument in isolation, or recursively as part
|
||||||
|
// of hashing the signature.
|
||||||
|
//
|
||||||
|
// We should never encounter a generic signature while hashing another
|
||||||
|
// generic signature, but defensively set sigTParams only if h.mask is
|
||||||
|
// unset.
|
||||||
|
tparams := t.TypeParams()
|
||||||
|
if h.sigTParams == nil && tparams.Len() != 0 {
|
||||||
|
h = Hasher{
|
||||||
|
// There may be something more efficient than discarding the existing
|
||||||
|
// memo, but it would require detecting whether types are 'tainted' by
|
||||||
|
// references to type parameters.
|
||||||
|
memo: make(map[types.Type]uint32),
|
||||||
|
// Re-using ptrMap ensures that pointer identity is preserved in this
|
||||||
|
// hasher.
|
||||||
|
ptrMap: h.ptrMap,
|
||||||
|
sigTParams: tparams,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < tparams.Len(); i++ {
|
||||||
|
tparam := tparams.At(i)
|
||||||
|
hash += 7 * h.Hash(tparam.Constraint())
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashFor computes the hash of t.
|
||||||
|
func (h Hasher) hashFor(t types.Type) uint32 {
|
||||||
|
// See Identical for rationale.
|
||||||
|
switch t := t.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
return uint32(t.Kind())
|
||||||
|
|
||||||
|
case *aliases.Alias:
|
||||||
|
return h.Hash(t.Underlying())
|
||||||
|
|
||||||
|
case *types.Array:
|
||||||
|
return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Slice:
|
||||||
|
return 9049 + 2*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Struct:
|
||||||
|
var hash uint32 = 9059
|
||||||
|
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if f.Anonymous() {
|
||||||
|
hash += 8861
|
||||||
|
}
|
||||||
|
hash += hashString(t.Tag(i))
|
||||||
|
hash += hashString(f.Name()) // (ignore f.Pkg)
|
||||||
|
hash += h.Hash(f.Type())
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
|
||||||
|
case *types.Pointer:
|
||||||
|
return 9067 + 2*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Signature:
|
||||||
|
return HashSig(h, t)
|
||||||
|
|
||||||
|
case *types.Union:
|
||||||
|
return h.hashUnion(t)
|
||||||
|
|
||||||
|
case *types.Interface:
|
||||||
|
// Interfaces are identical if they have the same set of methods, with
|
||||||
|
// identical names and types, and they have the same set of type
|
||||||
|
// restrictions. See go/types.identical for more details.
|
||||||
|
var hash uint32 = 9103
|
||||||
|
|
||||||
|
// Hash methods.
|
||||||
|
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||||
|
// Method order is not significant.
|
||||||
|
// Ignore m.Pkg().
|
||||||
|
m := t.Method(i)
|
||||||
|
// Use shallow hash on method signature to
|
||||||
|
// avoid anonymous interface cycles.
|
||||||
|
hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash type restrictions.
|
||||||
|
terms, err := typeparams.InterfaceTermSet(t)
|
||||||
|
// if err != nil t has invalid type restrictions.
|
||||||
|
if err == nil {
|
||||||
|
hash += h.hashTermSet(terms)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash
|
||||||
|
|
||||||
|
case *types.Map:
|
||||||
|
return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Chan:
|
||||||
|
return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
|
||||||
|
|
||||||
|
case *types.Named:
|
||||||
|
hash := h.hashPtr(t.Obj())
|
||||||
|
targs := t.TypeArgs()
|
||||||
|
for i := 0; i < targs.Len(); i++ {
|
||||||
|
targ := targs.At(i)
|
||||||
|
hash += 2 * h.Hash(targ)
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
|
||||||
|
case *types.TypeParam:
|
||||||
|
return h.hashTypeParam(t)
|
||||||
|
|
||||||
|
case *types.Tuple:
|
||||||
|
return h.hashTuple(t)
|
||||||
|
|
||||||
|
case interface{ Hash(h Hasher) uint32 }:
|
||||||
|
return t.Hash(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("%T: %v", t, t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
|
||||||
|
// See go/types.identicalTypes for rationale.
|
||||||
|
n := tuple.Len()
|
||||||
|
hash := 9137 + 2*uint32(n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
hash += 3 * h.Hash(tuple.At(i).Type())
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Hasher) hashUnion(t *types.Union) uint32 {
|
||||||
|
// Hash type restrictions.
|
||||||
|
terms, err := typeparams.UnionTermSet(t)
|
||||||
|
// if err != nil t has invalid type restrictions. Fall back on a non-zero
|
||||||
|
// hash.
|
||||||
|
if err != nil {
|
||||||
|
return 9151
|
||||||
|
}
|
||||||
|
return h.hashTermSet(terms)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Hasher) hashTermSet(terms []*types.Term) uint32 {
|
||||||
|
hash := 9157 + 2*uint32(len(terms))
|
||||||
|
for _, term := range terms {
|
||||||
|
// term order is not significant.
|
||||||
|
termHash := h.Hash(term.Type())
|
||||||
|
if term.Tilde() {
|
||||||
|
termHash *= 9161
|
||||||
|
}
|
||||||
|
hash += 3 * termHash
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashTypeParam returns a hash of the type parameter t, with a hash value
|
||||||
|
// depending on whether t is contained in h.sigTParams.
|
||||||
|
//
|
||||||
|
// If h.sigTParams is set and contains t, then we are in the process of hashing
|
||||||
|
// a signature, and the hash value of t must depend only on t's index and
|
||||||
|
// constraint: signatures are considered identical modulo type parameter
|
||||||
|
// renaming. To avoid infinite recursion, we only hash the type parameter
|
||||||
|
// index, and rely on types.Identical to handle signatures where constraints
|
||||||
|
// are not identical.
|
||||||
|
//
|
||||||
|
// Otherwise the hash of t depends only on t's pointer identity.
|
||||||
|
func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 {
|
||||||
|
if h.sigTParams != nil {
|
||||||
|
i := t.Index()
|
||||||
|
if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) {
|
||||||
|
return 9173 + 3*uint32(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return h.hashPtr(t.Obj())
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that
|
||||||
|
// pointers values are not dependent on the GC.
|
||||||
|
func (h Hasher) hashPtr(ptr interface{}) uint32 {
|
||||||
|
if hash, ok := h.ptrMap[ptr]; ok {
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
hash := uint32(reflect.ValueOf(ptr).Pointer())
|
||||||
|
h.ptrMap[ptr] = hash
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// shallowHash computes a hash of t without looking at any of its
|
||||||
|
// element Types, to avoid potential anonymous cycles in the types of
|
||||||
|
// interface methods.
|
||||||
|
//
|
||||||
|
// When an unnamed non-empty interface type appears anywhere among the
|
||||||
|
// arguments or results of an interface method, there is a potential
|
||||||
|
// for endless recursion. Consider:
|
||||||
|
//
|
||||||
|
// type X interface { m() []*interface { X } }
|
||||||
|
//
|
||||||
|
// The problem is that the Methods of the interface in m's result type
|
||||||
|
// include m itself; there is no mention of the named type X that
|
||||||
|
// might help us break the cycle.
|
||||||
|
// (See comment in go/types.identical, case *Interface, for more.)
|
||||||
|
func (h Hasher) shallowHash(t types.Type) uint32 {
|
||||||
|
// t is the type of an interface method (Signature),
|
||||||
|
// its params or results (Tuples), or their immediate
|
||||||
|
// elements (mostly Slice, Pointer, Basic, Named),
|
||||||
|
// so there's no need to optimize anything else.
|
||||||
|
switch t := t.(type) {
|
||||||
|
case *aliases.Alias:
|
||||||
|
return h.shallowHash(t.Underlying())
|
||||||
|
|
||||||
|
case *types.Signature:
|
||||||
|
var hash uint32 = 604171
|
||||||
|
if t.Variadic() {
|
||||||
|
hash *= 971767
|
||||||
|
}
|
||||||
|
// The Signature/Tuple recursion is always finite
|
||||||
|
// and invariably shallow.
|
||||||
|
return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results())
|
||||||
|
|
||||||
|
case *types.Tuple:
|
||||||
|
n := t.Len()
|
||||||
|
hash := 9137 + 2*uint32(n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
hash += 53471161 * h.shallowHash(t.At(i).Type())
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
|
||||||
|
case *types.Basic:
|
||||||
|
return 45212177 * uint32(t.Kind())
|
||||||
|
|
||||||
|
case *types.Array:
|
||||||
|
return 1524181 + 2*uint32(t.Len())
|
||||||
|
|
||||||
|
case *types.Slice:
|
||||||
|
return 2690201
|
||||||
|
|
||||||
|
case *types.Struct:
|
||||||
|
return 3326489
|
||||||
|
|
||||||
|
case *types.Pointer:
|
||||||
|
return 4393139
|
||||||
|
|
||||||
|
case *types.Union:
|
||||||
|
return 562448657
|
||||||
|
|
||||||
|
case *types.Interface:
|
||||||
|
return 2124679 // no recursion here
|
||||||
|
|
||||||
|
case *types.Map:
|
||||||
|
return 9109
|
||||||
|
|
||||||
|
case *types.Chan:
|
||||||
|
return 9127
|
||||||
|
|
||||||
|
case *types.Named:
|
||||||
|
return h.hashPtr(t.Obj())
|
||||||
|
|
||||||
|
case *types.TypeParam:
|
||||||
|
return h.hashPtr(t.Obj())
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("shallowHash: %T: %v", t, t))
|
||||||
|
}
|
||||||
@@ -34,3 +34,46 @@ func TestRuntime(t *testing.T) {
|
|||||||
cltest.Pkg(t, "github.com/goplus/llgo/internal/runtime", "../internal/runtime/llgo_autogen.ll")
|
cltest.Pkg(t, "github.com/goplus/llgo/internal/runtime", "../internal/runtime/llgo_autogen.ll")
|
||||||
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) {
|
||||||
|
var m typeutil.Map
|
||||||
|
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
|
||||||
|
m.Set(sig, 1)
|
||||||
|
csig := (*ssa.CFuncPtr)(sig)
|
||||||
|
m.Set(csig, 2)
|
||||||
|
if v := m.At(sig); v.(int) != 1 {
|
||||||
|
t.Fatal("At(sig):", v)
|
||||||
|
}
|
||||||
|
if v := m.At(csig); v.(int) != 2 {
|
||||||
|
t.Fatal("At(csig):", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|||||||
68
ssa/decl.go
68
ssa/decl.go
@@ -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
|
||||||
hasVArg bool
|
freeVars Expr
|
||||||
|
base int // base = 1 if hasFreeVars; base = 0 otherwise
|
||||||
|
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 {
|
||||||
|
|||||||
523
ssa/expr.go
523
ssa/expr.go
@@ -41,24 +41,23 @@ 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() Expr {
|
func (v Expr) Do(b Builder) Expr {
|
||||||
if vt := v.Type; vt.kind == vkDelayExpr {
|
switch vt := v.Type; vt.kind {
|
||||||
return vt.t.(delayExprTy)()
|
case vkDelayExpr:
|
||||||
|
return vt.raw.Type.(delayExprTy)()
|
||||||
|
case vkPhisExpr:
|
||||||
|
e := vt.raw.Type.(*phisExprTy)
|
||||||
|
return b.aggregateValue(e.Type, e.phis...)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// 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
|
||||||
@@ -73,6 +72,25 @@ func (p delayExprTy) String() string {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type phisExprTy struct {
|
||||||
|
phis []llvm.Value
|
||||||
|
Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p phisExprTy) Underlying() types.Type {
|
||||||
|
panic("don't call")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p phisExprTy) String() string {
|
||||||
|
return "phisExpr"
|
||||||
|
}
|
||||||
|
|
||||||
|
func phisExpr(t Type, phis []llvm.Value) Expr {
|
||||||
|
return Expr{Type: &aType{raw: rawType{&phisExprTy{phis, t}}, kind: vkPhisExpr}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// Null returns a null constant expression.
|
// Null returns a null constant expression.
|
||||||
func (p Program) Null(t Type) Expr {
|
func (p Program) Null(t Type) Expr {
|
||||||
return Expr{llvm.ConstNull(t.ll), t}
|
return Expr{llvm.ConstNull(t.ll), t}
|
||||||
@@ -123,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 {
|
||||||
@@ -145,7 +164,14 @@ 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.
|
||||||
|
func (b Builder) SizeOf(t Type, n ...int64) Expr {
|
||||||
|
prog := b.Prog
|
||||||
|
size := prog.SizeOf(t, n...)
|
||||||
|
return prog.IntVal(size, prog.Uintptr())
|
||||||
}
|
}
|
||||||
|
|
||||||
// CStr returns a c-style string constant expression.
|
// CStr returns a c-style string constant expression.
|
||||||
@@ -157,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
|
||||||
}
|
}
|
||||||
@@ -268,12 +294,17 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
|
|||||||
case isMathOp(op): // op: + - * / %
|
case isMathOp(op): // op: + - * / %
|
||||||
kind := x.kind
|
kind := x.kind
|
||||||
switch kind {
|
switch kind {
|
||||||
case vkString, vkComplex:
|
case vkString:
|
||||||
panic("todo")
|
if op == token.ADD {
|
||||||
}
|
pkg := b.Func.Pkg
|
||||||
idx := mathOpIdx(op, kind)
|
return b.InlineCall(pkg.rtFunc("StringCat"), x, y)
|
||||||
if llop := mathOpToLLVM[idx]; llop != 0 {
|
}
|
||||||
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
|
case vkComplex:
|
||||||
|
default:
|
||||||
|
idx := mathOpIdx(op, kind)
|
||||||
|
if llop := mathOpToLLVM[idx]; llop != 0 {
|
||||||
|
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case isLogicOp(op): // op: & | ^ << >> &^
|
case isLogicOp(op): // op: & | ^ << >> &^
|
||||||
if op == token.AND_NOT {
|
if op == token.AND_NOT {
|
||||||
@@ -324,10 +355,57 @@ func (b Builder) UnOp(op token.Token, x Expr) Expr {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
func llvmValues(vals []Expr) []llvm.Value {
|
func checkExpr(v Expr, t types.Type, b Builder) Expr {
|
||||||
ret := make([]llvm.Value, len(vals))
|
if t, ok := t.(*types.Struct); ok && isClosure(t) {
|
||||||
for i, v := range vals {
|
if v.kind != vkClosure {
|
||||||
ret[i] = v.impl
|
return b.Func.Pkg.closureStub(b, t, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
if n > 0 {
|
||||||
|
ret = make([]llvm.Value, len(vals)+base)
|
||||||
|
for idx, v := range vals {
|
||||||
|
i := base + idx
|
||||||
|
if i < n {
|
||||||
|
v = checkExpr(v, params.At(i).Type(), b)
|
||||||
|
}
|
||||||
|
ret[i] = v.impl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
ret := make([]llvm.Value, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
ret[i] = f(i).impl
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@@ -346,19 +424,65 @@ type Phi struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddIncoming adds incoming values to a phi node.
|
// AddIncoming adds incoming values to a phi node.
|
||||||
func (p Phi) AddIncoming(vals []Expr, bblks []BasicBlock) {
|
func (p Phi) AddIncoming(b Builder, bblks []BasicBlock, f func(i int) Expr) {
|
||||||
v := llvmValues(vals)
|
bs := llvmBlocks(bblks)
|
||||||
b := llvmBlocks(bblks)
|
if p.kind != vkPhisExpr { // normal phi node
|
||||||
p.impl.AddIncoming(v, b)
|
vs := llvmDelayValues(f, len(bblks))
|
||||||
|
p.impl.AddIncoming(vs, bs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e := p.raw.Type.(*phisExprTy)
|
||||||
|
phis := e.phis
|
||||||
|
vals := make([][]llvm.Value, len(phis))
|
||||||
|
for iblk, blk := range bblks {
|
||||||
|
last := blk.impl.LastInstruction()
|
||||||
|
b.impl.SetInsertPointBefore(last)
|
||||||
|
impl := b.impl
|
||||||
|
val := f(iblk).impl
|
||||||
|
for i := range phis {
|
||||||
|
if iblk == 0 {
|
||||||
|
vals[i] = make([]llvm.Value, len(bblks))
|
||||||
|
}
|
||||||
|
vals[i][iblk] = llvm.CreateExtractValue(impl, val, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, phi := range phis {
|
||||||
|
phi.AddIncoming(vals[i], bs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phi returns a phi node.
|
// Phi returns a phi node.
|
||||||
func (b Builder) Phi(t Type) Phi {
|
func (b Builder) Phi(t Type) Phi {
|
||||||
return Phi{Expr{llvm.CreatePHI(b.impl, t.ll), t}}
|
impl := b.impl
|
||||||
|
switch tund := t.raw.Type.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
kind := tund.Kind()
|
||||||
|
switch kind {
|
||||||
|
case types.String:
|
||||||
|
prog := b.Prog
|
||||||
|
phis := make([]llvm.Value, 2)
|
||||||
|
phis[0] = llvm.CreatePHI(impl, prog.tyVoidPtr())
|
||||||
|
phis[1] = llvm.CreatePHI(impl, prog.tyInt())
|
||||||
|
return Phi{phisExpr(t, phis)}
|
||||||
|
}
|
||||||
|
case *types.Struct:
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
phi := llvm.CreatePHI(impl, t.ll)
|
||||||
|
return Phi{Expr{phi, t}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Advance returns the pointer ptr advanced by offset bytes.
|
||||||
|
func (b Builder) Advance(ptr Expr, offset Expr) Expr {
|
||||||
|
if debugInstr {
|
||||||
|
log.Printf("Advance %v, %v\n", ptr.impl, offset.impl)
|
||||||
|
}
|
||||||
|
ret := llvm.CreateGEP(b.impl, b.Prog.tyInt8(), ptr.impl, []llvm.Value{offset.impl})
|
||||||
|
return Expr{ret, ptr.Type}
|
||||||
|
}
|
||||||
|
|
||||||
// Load returns the value at the pointer ptr.
|
// Load returns the value at the pointer ptr.
|
||||||
func (b Builder) Load(ptr Expr) Expr {
|
func (b Builder) Load(ptr Expr) Expr {
|
||||||
if debugInstr {
|
if debugInstr {
|
||||||
@@ -370,13 +494,61 @@ 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
|
||||||
|
func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr {
|
||||||
|
tll := t.ll
|
||||||
|
impl := b.impl
|
||||||
|
ptr := llvm.CreateAlloca(impl, tll)
|
||||||
|
for i, fld := range flds {
|
||||||
|
impl.CreateStore(fld, llvm.CreateStructGEP(impl, tll, ptr, i))
|
||||||
|
}
|
||||||
|
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
|
||||||
@@ -387,12 +559,6 @@ func (b Builder) Store(ptr, val Expr) Builder {
|
|||||||
//
|
//
|
||||||
// Type() returns a (possibly named) *types.Pointer.
|
// Type() returns a (possibly named) *types.Pointer.
|
||||||
//
|
//
|
||||||
// Pos() returns the position of the ast.SelectorExpr.Sel for the
|
|
||||||
// field, if explicit in the source. For implicit selections, returns
|
|
||||||
// the position of the inducing explicit selection. If produced for a
|
|
||||||
// struct literal S{f: e}, it returns the position of the colon; for
|
|
||||||
// S{e} it returns the start of expression e.
|
|
||||||
//
|
|
||||||
// Example printed form:
|
// Example printed form:
|
||||||
//
|
//
|
||||||
// t1 = &t0.name [#1]
|
// t1 = &t0.name [#1]
|
||||||
@@ -407,6 +573,20 @@ func (b Builder) FieldAddr(x Expr, idx int) Expr {
|
|||||||
return Expr{llvm.CreateStructGEP(b.impl, tstruc.ll, x.impl, idx), pt}
|
return Expr{llvm.CreateStructGEP(b.impl, tstruc.ll, x.impl, idx), pt}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Field instruction yields the value of Field of struct X.
|
||||||
|
func (b Builder) Field(x Expr, idx int) Expr {
|
||||||
|
if debugInstr {
|
||||||
|
log.Printf("Field %v, %d\n", x.impl, idx)
|
||||||
|
}
|
||||||
|
return b.getField(x, idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
// index `idx` of collection `x`. `idx` is an integer expression.
|
// index `idx` of collection `x`. `idx` is an integer expression.
|
||||||
//
|
//
|
||||||
@@ -426,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}
|
||||||
@@ -438,6 +618,64 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
|
|||||||
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, x.impl, indices), pt}
|
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, x.impl, indices), pt}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Index instruction yields element Index of collection X, an array,
|
||||||
|
// string or type parameter containing an array, a string, a pointer to an,
|
||||||
|
// array or a slice.
|
||||||
|
//
|
||||||
|
// Example printed form:
|
||||||
|
//
|
||||||
|
// t2 = t0[t1]
|
||||||
|
func (b Builder) Index(x, idx Expr, addr func(Expr) Expr) Expr {
|
||||||
|
if debugInstr {
|
||||||
|
log.Printf("Index %v, %v\n", x.impl, idx.impl)
|
||||||
|
}
|
||||||
|
prog := b.Prog
|
||||||
|
var telem Type
|
||||||
|
var ptr Expr
|
||||||
|
switch t := x.raw.Type.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
if t.Kind() != types.String {
|
||||||
|
panic(fmt.Errorf("invalid operation: cannot index %v", t))
|
||||||
|
}
|
||||||
|
telem = prog.rawType(types.Typ[types.Byte])
|
||||||
|
pkg := b.Func.Pkg
|
||||||
|
ptr = b.InlineCall(pkg.rtFunc("StringData"), x)
|
||||||
|
case *types.Array:
|
||||||
|
telem = prog.Index(x.Type)
|
||||||
|
if addr != nil {
|
||||||
|
ptr = addr(x)
|
||||||
|
} else {
|
||||||
|
size := b.SizeOf(telem, t.Len())
|
||||||
|
ptr = b.Alloca(size)
|
||||||
|
b.Store(ptr, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pt := prog.Pointer(telem)
|
||||||
|
indices := []llvm.Value{idx.impl}
|
||||||
|
buf := Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
|
||||||
|
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.
|
||||||
//
|
//
|
||||||
@@ -447,10 +685,6 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
|
|||||||
// Type() returns string if the type of X was string, otherwise a
|
// Type() returns string if the type of X was string, otherwise a
|
||||||
// *types.Slice with the same element type as X.
|
// *types.Slice with the same element type as X.
|
||||||
//
|
//
|
||||||
// Pos() returns the ast.SliceExpr.Lbrack if created by a x[:] slice
|
|
||||||
// operation, the ast.CompositeLit.Lbrace if created by a literal, or
|
|
||||||
// NoPos if not explicit in the source (e.g. a variadic argument slice).
|
|
||||||
//
|
|
||||||
// Example printed form:
|
// Example printed form:
|
||||||
//
|
//
|
||||||
// t1 = slice t0[1:]
|
// t1 = slice t0[1:]
|
||||||
@@ -459,21 +693,51 @@ 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
|
||||||
switch t := x.t.Underlying().(type) {
|
var nCap Expr
|
||||||
|
var nEltSize Expr
|
||||||
|
var base Expr
|
||||||
|
if low.IsNil() {
|
||||||
|
low = prog.IntVal(0, prog.Int())
|
||||||
|
}
|
||||||
|
switch t := x.raw.Type.Underlying().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
if t.Kind() != types.String {
|
||||||
|
panic(fmt.Errorf("invalid operation: cannot slice %v", t))
|
||||||
|
}
|
||||||
|
if high.IsNil() {
|
||||||
|
high = b.InlineCall(pkg.rtFunc("StringLen"), x)
|
||||||
|
}
|
||||||
|
ret.Type = x.Type
|
||||||
|
ret.impl = b.InlineCall(pkg.rtFunc("NewStringSlice"), x, low, high).impl
|
||||||
|
return
|
||||||
|
case *types.Slice:
|
||||||
|
nEltSize = b.SizeOf(prog.Index(x.Type))
|
||||||
|
nCap = b.InlineCall(pkg.rtFunc("SliceCap"), x)
|
||||||
|
if high.IsNil() {
|
||||||
|
high = b.InlineCall(pkg.rtFunc("SliceLen"), x)
|
||||||
|
}
|
||||||
|
ret.Type = x.Type
|
||||||
|
base = b.InlineCall(pkg.rtFunc("SliceData"), x)
|
||||||
case *types.Pointer:
|
case *types.Pointer:
|
||||||
telem := t.Elem()
|
telem := t.Elem()
|
||||||
switch te := telem.Underlying().(type) {
|
switch te := telem.Underlying().(type) {
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
ret.Type = prog.Type(types.NewSlice(te.Elem()))
|
elem := prog.rawType(te.Elem())
|
||||||
if low.IsNil() && high.IsNil() && max.IsNil() {
|
ret.Type = prog.Slice(elem)
|
||||||
n := prog.Val(int(te.Len()))
|
nEltSize = b.SizeOf(elem)
|
||||||
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice"), x, n, n).impl
|
nCap = prog.IntVal(uint64(te.Len()), prog.Int())
|
||||||
return ret
|
if high.IsNil() {
|
||||||
|
high = nCap
|
||||||
}
|
}
|
||||||
|
base = x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic("todo")
|
if max.IsNil() {
|
||||||
|
max = nCap
|
||||||
|
}
|
||||||
|
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice3"), base, nEltSize, nCap, low, high, max).impl
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -483,24 +747,51 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
|
|||||||
//
|
//
|
||||||
// t is a (possibly named) *types.Map.
|
// t is a (possibly named) *types.Map.
|
||||||
//
|
//
|
||||||
// Pos() returns the ast.CallExpr.Lparen, if created by make(map), or
|
|
||||||
// the ast.CompositeLit.Lbrack if created by a literal.
|
|
||||||
//
|
|
||||||
// Example printed form:
|
// Example printed form:
|
||||||
//
|
//
|
||||||
// t1 = make map[string]int t0
|
// t1 = make map[string]int t0
|
||||||
// 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
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The MakeSlice instruction yields a slice of length Len backed by a
|
||||||
|
// newly allocated array of length Cap.
|
||||||
|
//
|
||||||
|
// Both Len and Cap must be non-nil Values of integer type.
|
||||||
|
//
|
||||||
|
// (Alloc(types.Array) followed by Slice will not suffice because
|
||||||
|
// Alloc can only create arrays of constant length.)
|
||||||
|
//
|
||||||
|
// Type() returns a (possibly named) *types.Slice.
|
||||||
|
//
|
||||||
|
// Example printed form:
|
||||||
|
//
|
||||||
|
// t1 = make []string 1:int t0
|
||||||
|
// t1 = make StringSlice 1:int t0
|
||||||
|
func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) {
|
||||||
|
if debugInstr {
|
||||||
|
log.Printf("MakeSlice %v, %v, %v\n", t.RawType(), len.impl, cap.impl)
|
||||||
|
}
|
||||||
|
pkg := b.Func.Pkg
|
||||||
|
if cap.IsNil() {
|
||||||
|
cap = len
|
||||||
|
}
|
||||||
|
elemSize := b.SizeOf(b.Prog.Index(t))
|
||||||
|
size := b.BinOp(token.MUL, cap, elemSize)
|
||||||
|
ptr := b.InlineCall(pkg.rtFunc("AllocZ"), size)
|
||||||
|
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice"), ptr, len, cap).impl
|
||||||
|
ret.Type = t
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// The Alloc instruction reserves space for a variable of the given type,
|
// The Alloc instruction reserves space for a variable of the given type,
|
||||||
@@ -522,21 +813,20 @@ func (b Builder) MakeMap(t Type, nReserve 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
|
||||||
telem := t.Elem()
|
pkg := b.Func.Pkg
|
||||||
|
size := b.SizeOf(elem)
|
||||||
if heap {
|
if heap {
|
||||||
pkg := b.fn.pkg
|
ret = b.InlineCall(pkg.rtFunc("AllocZ"), size)
|
||||||
size := prog.sizs.Sizeof(telem)
|
|
||||||
ret = b.Call(pkg.rtFunc("Alloc"), prog.Val(uintptr(size)))
|
|
||||||
} else {
|
} else {
|
||||||
ret.impl = llvm.CreateAlloca(b.impl, prog.Type(telem).ll)
|
ret = Expr{llvm.CreateAlloca(b.impl, elem.ll), prog.VoidPtr()}
|
||||||
|
ret.impl = b.InlineCall(pkg.rtFunc("Zeroinit"), ret, size).impl
|
||||||
}
|
}
|
||||||
// TODO(xsw): zero-initialize
|
ret.Type = prog.Pointer(elem)
|
||||||
ret.Type = prog.Type(t)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,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)
|
||||||
@@ -603,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -637,15 +928,12 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
|
|||||||
// Conversions of untyped string/number/bool constants to a specific
|
// Conversions of untyped string/number/bool constants to a specific
|
||||||
// representation are eliminated during SSA construction.
|
// representation are eliminated during SSA construction.
|
||||||
//
|
//
|
||||||
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
|
|
||||||
// from an explicit conversion in the source.
|
|
||||||
//
|
|
||||||
// Example printed form:
|
// Example printed form:
|
||||||
//
|
//
|
||||||
// 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()
|
||||||
@@ -692,24 +980,21 @@ func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
|
|||||||
//
|
//
|
||||||
// NewConst(constant.MakeNil(), T, pos)
|
// NewConst(constant.MakeNil(), T, pos)
|
||||||
//
|
//
|
||||||
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
|
|
||||||
// from an explicit conversion in the source.
|
|
||||||
//
|
|
||||||
// Example printed form:
|
// Example printed form:
|
||||||
//
|
//
|
||||||
// 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 {
|
||||||
@@ -771,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:
|
||||||
@@ -813,21 +1098,57 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
|
|||||||
// t4 = t3()
|
// t4 = t3()
|
||||||
// t7 = invoke t5.Println(...t6)
|
// t7 = invoke t5.Println(...t6)
|
||||||
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
|
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
|
||||||
|
prog := b.Prog
|
||||||
if debugInstr {
|
if debugInstr {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
fmt.Fprint(&b, "Call ", fn.impl.Name())
|
name := fn.impl.Name()
|
||||||
|
if name == "" {
|
||||||
|
name = "closure"
|
||||||
|
}
|
||||||
|
fmt.Fprint(&b, "Call ", fn.kind, " ", fn.raw.Type, " ", name)
|
||||||
|
sep := ": "
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
fmt.Fprint(&b, ", ", arg.impl)
|
fmt.Fprint(&b, sep, arg.impl)
|
||||||
|
sep = ", "
|
||||||
}
|
}
|
||||||
log.Println(b.String())
|
log.Println(b.String())
|
||||||
}
|
}
|
||||||
switch t := fn.t.(type) {
|
var ll llvm.Type
|
||||||
case *types.Signature:
|
var data Expr
|
||||||
ret.Type = b.Prog.retType(t)
|
var sig *types.Signature
|
||||||
|
var raw = fn.raw.Type
|
||||||
|
switch fn.kind {
|
||||||
|
case vkClosure:
|
||||||
|
data = b.Field(fn, 1)
|
||||||
|
fn = b.Field(fn, 0)
|
||||||
|
raw = fn.raw.Type
|
||||||
|
fallthrough
|
||||||
|
case vkFuncPtr:
|
||||||
|
sig = raw.(*types.Signature)
|
||||||
|
ll = prog.FuncDecl(sig, InC).ll
|
||||||
|
case vkFuncDecl:
|
||||||
|
sig = raw.(*types.Signature)
|
||||||
|
ll = fn.ll
|
||||||
default:
|
default:
|
||||||
panic("todo")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
ret.impl = llvm.CreateCall(b.impl, fn.ll, fn.impl, llvmValues(args))
|
ret.Type = prog.retType(sig)
|
||||||
|
ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -842,9 +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 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:
|
||||||
|
if t.Kind() == types.String {
|
||||||
|
return b.InlineCall(b.Func.Pkg.rtFunc("StringLen"), arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "cap":
|
||||||
|
if len(args) == 1 {
|
||||||
|
arg := args[0]
|
||||||
|
switch arg.raw.Type.Underlying().(type) {
|
||||||
|
case *types.Slice:
|
||||||
|
return b.InlineCall(b.Func.Pkg.rtFunc("SliceCap"), arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
139
ssa/package.go
139
ssa/package.go
@@ -17,9 +17,9 @@
|
|||||||
package ssa
|
package ssa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/constant"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"runtime"
|
"log"
|
||||||
|
|
||||||
"github.com/goplus/llvm"
|
"github.com/goplus/llvm"
|
||||||
"golang.org/x/tools/go/types/typeutil"
|
"golang.org/x/tools/go/types/typeutil"
|
||||||
@@ -96,9 +96,9 @@ 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
|
||||||
@@ -123,6 +123,7 @@ type aProgram struct {
|
|||||||
|
|
||||||
anyTy Type
|
anyTy Type
|
||||||
voidTy Type
|
voidTy Type
|
||||||
|
voidPtr Type
|
||||||
boolTy Type
|
boolTy Type
|
||||||
cstrTy Type
|
cstrTy Type
|
||||||
stringTy Type
|
stringTy Type
|
||||||
@@ -141,16 +142,19 @@ func NewProgram(target *Target) Program {
|
|||||||
if target == nil {
|
if target == nil {
|
||||||
target = &Target{}
|
target = &Target{}
|
||||||
}
|
}
|
||||||
arch := target.GOARCH
|
|
||||||
if arch == "" {
|
|
||||||
arch = runtime.GOARCH
|
|
||||||
}
|
|
||||||
ctx := llvm.NewContext()
|
ctx := llvm.NewContext()
|
||||||
sizes := types.SizesFor("gc", arch)
|
|
||||||
// TODO(xsw): Finalize may cause panic, so comment it.
|
|
||||||
// ctx.Finalize()
|
|
||||||
td := llvm.NewTargetData("") // TODO(xsw): target config
|
td := llvm.NewTargetData("") // TODO(xsw): target config
|
||||||
return &aProgram{ctx: ctx, sizs: sizes, target: target, td: td}
|
/*
|
||||||
|
arch := target.GOARCH
|
||||||
|
if arch == "" {
|
||||||
|
arch = runtime.GOARCH
|
||||||
|
}
|
||||||
|
sizes := types.SizesFor("gc", arch)
|
||||||
|
|
||||||
|
// TODO(xsw): Finalize may cause panic, so comment it.
|
||||||
|
ctx.Finalize()
|
||||||
|
*/
|
||||||
|
return &aProgram{ctx: ctx, gocvt: newGoTypes(), target: target, td: td}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRuntime sets the runtime.
|
// SetRuntime sets the runtime.
|
||||||
@@ -178,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 {
|
||||||
@@ -218,38 +224,46 @@ 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 {
|
||||||
|
if p.voidPtr == nil {
|
||||||
|
p.voidPtr = p.rawType(types.Typ[types.UnsafePointer])
|
||||||
|
}
|
||||||
|
return p.voidPtr
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
}
|
}
|
||||||
@@ -257,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
|
||||||
}
|
}
|
||||||
@@ -265,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
|
||||||
}
|
}
|
||||||
@@ -273,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
|
||||||
}
|
}
|
||||||
@@ -281,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
|
||||||
}
|
}
|
||||||
@@ -297,22 +311,25 @@ func (p Program) Float64() Type {
|
|||||||
// initializer) and "init#%d", the nth declared init function,
|
// initializer) and "init#%d", the nth declared init function,
|
||||||
// and unspecified other things too.
|
// and unspecified other things too.
|
||||||
type aPackage struct {
|
type aPackage struct {
|
||||||
mod llvm.Module
|
mod llvm.Module
|
||||||
fns map[string]Function
|
vars map[string]Global
|
||||||
vars map[string]Global
|
fns map[string]Function
|
||||||
prog Program
|
stubs map[string]Function
|
||||||
|
Prog Program
|
||||||
}
|
}
|
||||||
|
|
||||||
type Package = *aPackage
|
type Package = *aPackage
|
||||||
|
|
||||||
|
/*
|
||||||
// NewConst creates a new named constant.
|
// NewConst creates a new named constant.
|
||||||
func (p Package) NewConst(name string, val constant.Value) NamedConst {
|
func (p Package) NewConst(name string, val constant.Value) NamedConst {
|
||||||
return &aNamedConst{}
|
return &aNamedConst{}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// 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
|
||||||
@@ -325,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.llvmSignature(sig, false)
|
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.
|
||||||
|
|||||||
106
ssa/ssa_test.go
106
ssa/ssa_test.go
@@ -21,24 +21,72 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/goplus/llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
func TestClosureCtx(t *testing.T) {
|
||||||
func TestMakeInterface(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
b.MakeInterface(types.NewInterfaceType(nil, nil), Expr{}, true).Do(true)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func TestDelayExpr(t *testing.T) {
|
|
||||||
a := delayExprTy(nil)
|
|
||||||
_ = a.String()
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r == nil {
|
if r := recover(); r == nil {
|
||||||
t.Log("TestDelayExpr: no error?")
|
t.Log("closureCtx: no error?")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
a.Underlying()
|
var f aFunction
|
||||||
|
f.closureCtx(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTypes(t *testing.T) {
|
||||||
|
ctx := llvm.NewContext()
|
||||||
|
llvmIntType(ctx, 4)
|
||||||
|
|
||||||
|
intT := types.NewVar(0, nil, "", types.Typ[types.Int])
|
||||||
|
ret := types.NewTuple(intT, intT)
|
||||||
|
sig := types.NewSignatureType(nil, nil, nil, nil, ret, false)
|
||||||
|
prog := NewProgram(nil)
|
||||||
|
prog.retType(sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndexType(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Log("indexType: no error?")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
indexType(types.Typ[types.Int])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCvtType(t *testing.T) {
|
||||||
|
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() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Log("cvtType: no error?")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
gt.cvtType(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserdefExpr(t *testing.T) {
|
||||||
|
a := delayExprTy(nil)
|
||||||
|
b := &phisExprTy{}
|
||||||
|
_ = a.String()
|
||||||
|
_ = b.String()
|
||||||
|
test := func(a types.Type) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Log("TestUserdefExpr: no error?")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
a.Underlying()
|
||||||
|
}
|
||||||
|
test(a)
|
||||||
|
test(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAny(t *testing.T) {
|
func TestAny(t *testing.T) {
|
||||||
@@ -64,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"
|
||||||
@@ -81,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"
|
||||||
@@ -98,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"
|
||||||
|
|
||||||
@@ -115,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")
|
||||||
}
|
}
|
||||||
@@ -133,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")
|
||||||
}
|
}
|
||||||
@@ -155,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"
|
||||||
@@ -175,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"
|
||||||
@@ -196,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()
|
||||||
|
|
||||||
@@ -230,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'
|
||||||
@@ -251,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'
|
||||||
@@ -270,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)
|
||||||
@@ -305,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"
|
||||||
|
|
||||||
@@ -321,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)
|
||||||
@@ -344,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)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package ssa
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/types"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/goplus/llvm"
|
"github.com/goplus/llvm"
|
||||||
@@ -49,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,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 {
|
||||||
@@ -73,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()
|
||||||
}
|
}
|
||||||
@@ -100,15 +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:
|
||||||
b.impl.CreateAggregateRet(llvmValues(results))
|
tret := b.Func.raw.Type.(*types.Signature).Results()
|
||||||
|
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 {
|
||||||
@@ -119,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 {
|
||||||
|
|||||||
177
ssa/type.go
177
ssa/type.go
@@ -40,29 +40,16 @@ const (
|
|||||||
vkString
|
vkString
|
||||||
vkBool
|
vkBool
|
||||||
vkPtr
|
vkPtr
|
||||||
vkFunc
|
vkFuncDecl
|
||||||
|
vkFuncPtr
|
||||||
|
vkClosure
|
||||||
vkTuple
|
vkTuple
|
||||||
vkDelayExpr = -1
|
vkDelayExpr = -1
|
||||||
|
vkPhisExpr = -2
|
||||||
)
|
)
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
func indexType(t types.Type) types.Type {
|
func indexType(t types.Type) types.Type {
|
||||||
switch t := t.(type) {
|
switch t := t.(type) {
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
@@ -78,73 +65,64 @@ 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
|
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):
|
||||||
|
// how to generate platform independent code?
|
||||||
|
func (p Program) SizeOf(typ Type, n ...int64) uint64 {
|
||||||
|
size := p.td.TypeAllocSize(typ.ll)
|
||||||
|
if len(n) != 0 {
|
||||||
|
size *= uint64(n[0])
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
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()
|
||||||
return p.Type(tunder.(*types.Struct).Field(i).Type())
|
tfld := tunder.(*types.Struct).Field(i).Type()
|
||||||
|
return p.rawType(tfld)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Program) Type(typ types.Type) Type {
|
func (p Program) rawType(raw types.Type) Type {
|
||||||
if sig, ok := typ.(*types.Signature); ok { // should methodToFunc
|
if v := p.typs.At(raw); v != nil {
|
||||||
return p.llvmSignature(sig, true)
|
|
||||||
}
|
|
||||||
if v := p.typs.At(typ); 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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Program) llvmSignature(sig *types.Signature, isPtr bool) Type {
|
|
||||||
sig = methodToFunc(sig)
|
|
||||||
if v := p.typs.At(sig); v != nil {
|
|
||||||
return v.(Type)
|
|
||||||
}
|
|
||||||
ret := p.toLLVMFunc(sig, isPtr)
|
|
||||||
p.typs.Set(sig, ret)
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,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:
|
||||||
@@ -249,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}
|
||||||
@@ -258,35 +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: // represents a C function pointer in raw type
|
||||||
|
return &aType{p.toLLVMFuncPtr(t), typ, vkFuncPtr}
|
||||||
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
|
||||||
@@ -300,13 +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, isPtr bool) Type {
|
func (p Program) toLLVMFunc(sig *types.Signature) llvm.Type {
|
||||||
tParams := sig.Params()
|
tParams := sig.Params()
|
||||||
n := tParams.Len()
|
n := tParams.Len()
|
||||||
hasVArg := HasVArg(tParams, n)
|
hasVArg := HasVArg(tParams, n)
|
||||||
@@ -320,36 +316,37 @@ func (p Program) toLLVMFunc(sig *types.Signature, isPtr bool) Type {
|
|||||||
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 isPtr {
|
|
||||||
ft = llvm.PointerType(ft, 0)
|
|
||||||
}
|
|
||||||
return &aType{ft, sig, vkFunc}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
254
ssa/type_cvt.go
Normal file
254
ssa/type_cvt.go
Normal 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
262
x/clang/ast/ast.go
Normal 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
@@ -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
122
x/clang/parser/pages.go
Normal 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
104
x/clang/parser/parse.go
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
28
x/clang/pathutil/pathutil.go
Normal file
28
x/clang/pathutil/pathutil.go
Normal 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)
|
||||||
|
}
|
||||||
99
x/clang/preprocessor/preprocessor.go
Normal file
99
x/clang/preprocessor/preprocessor.go
Normal 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
251
x/clang/types/parser/_parser_test.go
Normal file
251
x/clang/types/parser/_parser_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
597
x/clang/types/parser/parser.go
Normal file
597
x/clang/types/parser/parser.go
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
303
x/clang/types/scanner/scanner.go
Normal file
303
x/clang/types/scanner/scanner.go
Normal 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
99
x/clang/types/types.go
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
Reference in New Issue
Block a user