Compare commits

...

139 Commits

Author SHA1 Message Date
xushiwei
aa1d0b307b Merge pull request #90 from xushiwei/q
llvm v0.7.3
2024-05-01 18:15:05 +08:00
xushiwei
8a09d8b442 update llvm 2024-05-01 18:12:42 +08:00
xushiwei
7b99470bfa llvm v0.7.3 2024-05-01 18:11:58 +08:00
xushiwei
30192da7cf Merge pull request #89 from xushiwei/q
build: make empty runtime.init if no runtime needed
2024-05-01 17:45:55 +08:00
xushiwei
66337e0975 build: make empty runtime.init if no runtime needed 2024-05-01 17:44:12 +08:00
xushiwei
6e4a44438b Merge pull request #88 from xushiwei/q
llgo/ssa: builder.Alloc sizeof(t) bugfix
2024-05-01 17:01:51 +08:00
xushiwei
3cc3b864af fix Alloc size 2024-05-01 16:59:42 +08:00
xushiwei
62f6542ca9 llgo/ssa: builder.Alloc sizeof(t) bugfix 2024-05-01 16:56:26 +08:00
xushiwei
8c25848e1b Merge pull request #87 from xushiwei/q
cl: callRuntimeInit; runtime: TracePanic; cl: _testrt/gblarray (todo)
2024-05-01 16:10:09 +08:00
xushiwei
e112d8277f cl: _testrt/gblarray 2024-05-01 16:06:49 +08:00
xushiwei
014d0262da build: fix link runtime multiple times 2024-05-01 13:30:13 +08:00
xushiwei
8ae97f73d0 debug why runtime.Basic doesn't work 2024-05-01 12:32:09 +08:00
xushiwei
666808b427 cl: callRuntimeInit; runtime: TracePanic 2024-05-01 11:03:52 +08:00
xushiwei
f208db48a1 Merge pull request #86 from xushiwei/q
runtime: MakeSmallMap
2024-05-01 07:40:24 +08:00
xushiwei
673b9d9a5c runtime.MakeSmallMap 2024-05-01 07:37:38 +08:00
xushiwei
ed8ffb228b runtime: MakeMap 2024-05-01 07:26:51 +08:00
xushiwei
d5a3ff6fb9 Merge pull request #85 from xushiwei/q
llgo/ssa: allocaCStr; runtime: String
2024-04-30 23:21:49 +08:00
xushiwei
d3fddfb634 mv _testcgo => _testrt 2024-04-30 23:18:18 +08:00
xushiwei
f7a54e3377 llgo/ssa: builder.Str bugfix; runtime: NewString 2024-04-30 18:42:40 +08:00
xushiwei
d62bf858dd llgo/ssa: Go const string 2024-04-30 18:37:31 +08:00
xushiwei
ae0906d322 llgo/ssa: allocaCStr; runtime: String 2024-04-30 18:22:56 +08:00
xushiwei
c6cb2931e1 Merge pull request #84 from xushiwei/q
llgo/ssa: Slice, IndexAddr bugfix
2024-04-30 16:22:01 +08:00
xushiwei
0eac576171 Merge pull request #83 from visualfc/error
build: buildAllPkgs dump error
2024-04-30 16:21:09 +08:00
xushiwei
0ee96db260 IndexAddr: default as ptr 2024-04-30 16:19:25 +08:00
xushiwei
40a9e00d4c llgo/ssa: Slice, IndexAddr bugfix 2024-04-30 16:15:36 +08:00
visualfc
d8615330ad build: buildAllPkgs dump error 2024-04-30 15:58:01 +08:00
xushiwei
02afd1d73a Merge pull request #82 from visualfc/slice
ssa: fix newSlice.type & const
2024-04-30 14:33:12 +08:00
visualfc
4eedb4fb69 cltest: SetRuntime 2024-04-30 14:16:46 +08:00
visualfc
2d6869bdbd ssa: fix newSlice.type & const 2024-04-30 14:14:28 +08:00
xushiwei
c0a9848cfb Merge pull request #81 from xushiwei/q
llgo/ssa,runtime: Slice; llgo/ssa: phi node
2024-04-30 11:36:11 +08:00
xushiwei
40cc7b2a85 llgen runtime 2024-04-30 11:31:39 +08:00
xushiwei
dfaae24fea update llvm 2024-04-30 11:26:40 +08:00
xushiwei
839ad8ba2b cl: _testcgo/sum 2024-04-30 11:19:42 +08:00
xushiwei
e757c66354 llgo/ssa: phi node 2024-04-30 10:34:10 +08:00
xushiwei
3ec6da002a Merge remote-tracking branch 'gop/main' into q 2024-04-30 08:27:57 +08:00
xushiwei
190b029154 Merge pull request #80 from visualfc/const
cl: compileValue check types.Default for const
2024-04-30 08:26:23 +08:00
xushiwei
0bfc269652 llgo/ssa,runtime: Slice 2024-04-30 08:23:55 +08:00
visualfc
b5961f0807 cl/_testdata: add untyped test 2024-04-30 08:14:08 +08:00
visualfc
a792e312db cl: compileValue check types.Default for const 2024-04-30 08:13:39 +08:00
xushiwei
04f81f3dbb Merge pull request #79 from xushiwei/q
ignore .DS_Store
2024-04-29 23:43:03 +08:00
xushiwei
91401c4571 ignore .DS_Store 2024-04-29 23:42:37 +08:00
xushiwei
18fe26e853 Merge pull request #77 from visualfc/float
ssa: builder.const add float
2024-04-29 23:09:22 +08:00
xushiwei
f895ca1fe6 Merge pull request #78 from xushiwei/q
llgo/ssa: MakeInterface
2024-04-29 23:04:59 +08:00
xushiwei
ad28ed3154 cl: _testcgo/any 2024-04-29 22:58:25 +08:00
xushiwei
f64abf37ab llgo/ssa: MakeInterface 2024-04-29 22:57:40 +08:00
visualfc
acc5de8d14 ssa: builder.const add float 2024-04-29 22:19:08 +08:00
xushiwei
85bb1302ca Merge pull request #76 from xushiwei/q
llgen bugfix
2024-04-29 20:52:58 +08:00
xushiwei
fc893ba498 llgen bugfix 2024-04-29 20:51:27 +08:00
xushiwei
7527b326d7 Merge pull request #75 from xushiwei/q
cl: support llgo.cstr
2024-04-29 19:01:01 +08:00
xushiwei
a6d31ad8b8 TestErrAlloca 2024-04-29 18:55:09 +08:00
xushiwei
664c3fcce3 compileInstrOrValue 2024-04-29 18:46:13 +08:00
xushiwei
e1d1d6a2d9 llgo/ssa: unreachable 2024-04-29 18:33:02 +08:00
xushiwei
8c1b8ad945 update llvm 2024-04-29 18:12:15 +08:00
xushiwei
ae5efdf16c disable ArrayAlloca 2024-04-29 18:01:03 +08:00
xushiwei
2b82af519c llgo/ssa: Alloca, ArrayAlloca 2024-04-29 17:58:10 +08:00
xushiwei
7a347d4563 update llvm 2024-04-29 14:39:37 +08:00
xushiwei
1610894a80 llgo/ssa: b.CString 2024-04-29 14:34:26 +08:00
xushiwei
4eb2ddaf15 cl: support llgo.cstr 2024-04-29 13:59:06 +08:00
xushiwei
99a86d8d4e Merge pull request #74 from xushiwei/q
build: skip PkgDeclOnly; cl: skip init if PkgNoInit
2024-04-29 12:04:07 +08:00
xushiwei
637db665c3 TestPkgKindOf 2024-04-29 11:58:48 +08:00
xushiwei
bb1da81a01 build: skip PkgDeclOnly 2024-04-29 11:34:59 +08:00
xushiwei
d4ddc61a9f cl: TestPkgNoInit/TestPkgKind 2024-04-29 10:06:47 +08:00
xushiwei
6ebbc92c4f runtime.ll 2024-04-29 09:53:48 +08:00
xushiwei
b45172bef1 cl: pkgKind = normal/noinit/decl 2024-04-29 09:51:32 +08:00
xushiwei
695d3f3327 Merge pull request #73 from xushiwei/q
llvm v0.7.2
2024-04-29 03:50:05 +08:00
xushiwei
7979cfcb06 update llgo_autogen.ll 2024-04-29 03:45:52 +08:00
xushiwei
2986cb0c5f llvm v0.7.2 2024-04-29 03:43:38 +08:00
xushiwei
5a175955a9 Merge pull request #72 from xushiwei/q
llgo/ssa: use b.CreateUnreachable to panic
2024-04-29 03:40:05 +08:00
xushiwei
cb507f43a7 llgo/ssa: use b.CreateUnreachable to panic 2024-04-29 03:36:19 +08:00
xushiwei
8b148d72c2 Merge pull request #71 from xushiwei/q
cl: don't need to compile alias type
2024-04-29 02:58:22 +08:00
xushiwei
616596e571 cl: don't need to compile alias type 2024-04-29 02:56:21 +08:00
xushiwei
2849fe4841 Merge pull request #70 from xushiwei/q
llgo/ssa: llvmSignature/castPtr/castInt bugfix; link: runtime
2024-04-29 01:44:01 +08:00
xushiwei
98065e80d0 TestRuntime 2024-04-29 01:42:00 +08:00
xushiwei
286b520d83 build: better error messages 2024-04-29 01:34:21 +08:00
xushiwei
769b93a277 build: mark need runtime 2024-04-29 00:49:17 +08:00
xushiwei
c30ed1b3c8 llgo/ssa: llvmSignature/castPtr/castInt bugfix; link: runtime 2024-04-29 00:16:00 +08:00
xushiwei
78d7f984d1 Merge pull request #69 from xushiwei/q
llgen: smart fname of outFile (llgo_autogen.ll or out.ll)
2024-04-28 23:12:15 +08:00
xushiwei
e88f7e6659 llgen: smart fname of outFile (llgo_autogen.ll or out.ll) 2024-04-28 23:07:59 +08:00
xushiwei
e8ff879943 Merge pull request #68 from xushiwei/q
cl: compileInstrOrValue bugfix
2024-04-28 22:40:36 +08:00
xushiwei
f09d5bd155 TestIsAny, TestIntVal 2024-04-28 22:38:04 +08:00
xushiwei
53e73fc622 x 2024-04-28 22:24:03 +08:00
xushiwei
ba94d6f04e cl: compileInstrOrValue bugfix 2024-04-28 22:20:46 +08:00
xushiwei
449f91ab14 Merge pull request #67 from xushiwei/q
llgo/ssa.SetRuntime: prevent multiple loading of runtime
2024-04-28 14:02:54 +08:00
xushiwei
70623dd554 llgo/ssa.SetRuntime: prevent multiple loading of runtime 2024-04-28 14:00:29 +08:00
xushiwei
35dc6dcd85 Merge pull request #66 from xushiwei/q
runtime
2024-04-28 12:24:59 +08:00
xushiwei
41dfafe957 TestRuntime 2024-04-28 12:22:56 +08:00
xushiwei
2cc1bdee19 llgo/ssa: pkg.NewFunc bugfix 2024-04-28 12:09:47 +08:00
xushiwei
eb4146d80d llgo/ssa: Alloc bugfix (heap) 2024-04-28 10:39:19 +08:00
xushiwei
0d68066086 runtime: MakeAnyString 2024-04-28 10:29:06 +08:00
xushiwei
7039cb3bc2 llgo/ssa: support string/cstring; panic 2024-04-28 09:55:54 +08:00
xushiwei
510f2f4769 runtime: Alloc 2024-04-28 07:08:33 +08:00
xushiwei
5415f68c1b llgo/ssa: Alloc, BinOp(vkPtr) 2024-04-28 07:08:01 +08:00
xushiwei
475f0fa2ff llgo/ssa: ChangeType, Convert 2024-04-28 06:23:21 +08:00
xushiwei
c58b1140d8 Merge pull request #65 from xushiwei/q
llgo build/install/run: link runtime
2024-04-28 06:21:57 +08:00
xushiwei
c97c1e97b9 llgo build/install/run: link runtime 2024-04-27 22:13:40 +08:00
xushiwei
ba3d82e5e5 Merge pull request #64 from xushiwei/q
cl: _testcgo/any
2024-04-27 21:35:17 +08:00
xushiwei
d432899b42 TestAny, TestDelayExpr 2024-04-27 21:32:48 +08:00
xushiwei
08da38a609 cl: _testcgo/any 2024-04-27 20:45:55 +08:00
xushiwei
6a3eb2f2f9 ssa: rtIface, rtSlice 2024-04-27 18:13:16 +08:00
xushiwei
6a02c3ac4c llgo/ssa: rtType, rtFunc 2024-04-27 17:39:25 +08:00
xushiwei
f1761c0c9c llgo/internal/runtime 2024-04-27 13:57:21 +08:00
xushiwei
39076c75cf Merge pull request #63 from xushiwei/q
cl: _testcgo/typalias
2024-04-27 10:10:16 +08:00
xushiwei
3be8cacc24 cl: _testcgo/typalias 2024-04-27 10:06:59 +08:00
xushiwei
46a9df47e4 cl: _testcgo/typalias 2024-04-27 08:33:49 +08:00
xushiwei
d44a31cc62 Merge pull request #62 from xushiwei/q
cl: _testcgo/struct
2024-04-27 08:21:00 +08:00
xushiwei
00b2fd1479 fix TestNamedStruct 2024-04-27 08:17:46 +08:00
xushiwei
5f08e7a612 cl: _testcgo/struct 2024-04-27 07:47:10 +08:00
xushiwei
5c8725373a Merge pull request #61 from xushiwei/q
llgo build/install/run: ParseArgs bugfix
2024-04-27 06:43:11 +08:00
xushiwei
c455f6e730 SkipFlagArgs 2024-04-27 06:41:24 +08:00
xushiwei
3cc83b8ec4 llgo build/install/run: SkipArgs bugfix 2024-04-27 06:39:09 +08:00
xushiwei
f74de76d70 Merge pull request #60 from xushiwei/q
llgo build bugfix: when len(initial)==1
2024-04-26 20:30:22 +08:00
xushiwei
ea8ddc6451 llgo build bugfix: when len(initial)==1 2024-04-26 20:28:06 +08:00
xushiwei
5f36c37cf2 Merge pull request #59 from xushiwei/q
llgo clean
2024-04-26 20:10:15 +08:00
xushiwei
2ad2873278 llgo clean 2024-04-26 20:06:54 +08:00
xushiwei
3855895808 Merge pull request #58 from xushiwei/q
cl: go:linkname specifies call convention by C.xxx
2024-04-26 13:27:55 +08:00
xushiwei
f86cd74a98 cl: go:linkname specifies call convention by C.xxx 2024-04-26 13:09:24 +08:00
xushiwei
1e6ecbadcd Merge pull request #57 from xushiwei/q
cl: fake libc
2024-04-26 05:53:19 +08:00
xushiwei
33716a3385 cl: fake libc 2024-04-26 05:51:30 +08:00
xushiwei
252f0bf967 Merge pull request #56 from xushiwei/q
cl: TestImport/TestVarOf
2024-04-26 05:40:50 +08:00
xushiwei
2c3e1d1055 cl: TestImport/TestVarOf 2024-04-26 05:39:15 +08:00
xushiwei
aafa639bf1 Merge pull request #55 from xushiwei/q
llgo: use -Wno-override-module to disable clang warning
2024-04-26 05:09:25 +08:00
xushiwei
815a8a74fc llgo: use -Wno-override-module to disable clang warning 2024-04-26 05:05:59 +08:00
xushiwei
840ea80e20 Merge pull request #54 from xushiwei/q
llgo run: strlen
2024-04-26 04:47:24 +08:00
xushiwei
773ae2c8c6 llgo run: strlen 2024-04-26 04:44:49 +08:00
xushiwei
4aadb4b86f Merge pull request #53 from xushiwei/q
cl: _testcgo/strlen
2024-04-26 03:28:08 +08:00
xushiwei
91d1d71f6d ssa: temp disable Finalize; cl: decls sort by name 2024-04-26 03:25:11 +08:00
xushiwei
a3d6a94600 cl: _testcgo/strlen 2024-04-26 02:40:36 +08:00
xushiwei
43ae7a23b2 cl: _testcgo/strlen - todo 2024-04-26 02:05:49 +08:00
xushiwei
b6005886fa Merge pull request #52 from xushiwei/q
cl: _testdata/ptrmthd
2024-04-26 00:34:59 +08:00
xushiwei
28dd34a136 cl: _testdata/ptrmthd 2024-04-26 00:31:02 +08:00
xushiwei
3a3c263203 Merge pull request #51 from xushiwei/q
cl: _testdata/method
2024-04-25 21:47:36 +08:00
xushiwei
87b7ecd1d6 cl: _testdata/method 2024-04-25 21:44:23 +08:00
xushiwei
f5a309b5ad Merge pull request #50 from xushiwei/q
cl: _testdata/printval
2024-04-25 14:50:30 +08:00
xushiwei
0f00add402 cl: pathOf(pkg) 2024-04-25 14:30:02 +08:00
xushiwei
1014fa53dd cl: _testdata/printval 2024-04-25 14:25:14 +08:00
xushiwei
04568835bd Merge pull request #49 from xushiwei/q
codecov
2024-04-25 07:39:44 +08:00
xushiwei
e4f8edc07c codecov 2024-04-25 07:37:04 +08:00
xushiwei
a8533d1677 Merge pull request #48 from xushiwei/q
llgo/cl/cltest
2024-04-25 07:31:31 +08:00
xushiwei
edcb66afb7 llgo/cl/cltest 2024-04-25 07:29:45 +08:00
88 changed files with 6993 additions and 396 deletions

View File

@@ -60,4 +60,10 @@ jobs:
run: go build -v ./... run: go build -v ./...
- name: Test - name: Test
run: go test -v ./... run: go test -v -coverprofile="coverage.txt" -covermode=atomic ./...
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: goplus/llgo

5
.gitignore vendored
View File

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

View File

@@ -4,7 +4,8 @@ llgo - A Go compiler based on LLVM
[![Build Status](https://github.com/goplus/llgo/actions/workflows/go.yml/badge.svg)](https://github.com/goplus/llgo/actions/workflows/go.yml) [![Build Status](https://github.com/goplus/llgo/actions/workflows/go.yml/badge.svg)](https://github.com/goplus/llgo/actions/workflows/go.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/goplus/llgo)](https://goreportcard.com/report/github.com/goplus/llgo) [![Go Report Card](https://goreportcard.com/badge/github.com/goplus/llgo)](https://goreportcard.com/report/github.com/goplus/llgo)
[![GitHub release](https://img.shields.io/github/v/tag/goplus/llgo.svg?label=release)](https://github.com/goplus/llgo/releases) [![GitHub release](https://img.shields.io/github/v/tag/goplus/llgo.svg?label=release)](https://github.com/goplus/llgo/releases)
[![GoDoc](https://pkg.go.dev/badge/github.com/goplus/llgo.svg)](https://pkg.go.dev/github.com/goplus/llgo)
<!--
[![Coverage Status](https://codecov.io/gh/goplus/llgo/branch/main/graph/badge.svg)](https://codecov.io/gh/goplus/llgo) [![Coverage Status](https://codecov.io/gh/goplus/llgo/branch/main/graph/badge.svg)](https://codecov.io/gh/goplus/llgo)
--> [![GoDoc](https://pkg.go.dev/badge/github.com/goplus/llgo.svg)](https://pkg.go.dev/github.com/goplus/llgo)
[![Language](https://img.shields.io/badge/language-Go+-blue.svg)](https://github.com/goplus/gop)
This is a Go compiler based on LLVM in order to better integrate Go with the C ecosystem. It's a subproject of [the Go+ project](https://github.com/goplus/gop).

View File

@@ -0,0 +1,57 @@
/*
* 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 main
import (
"fmt"
"os"
"strings"
"github.com/goplus/llgo/internal/llgen"
"github.com/goplus/mod"
)
func main() {
dir, _, err := mod.FindGoMod(".")
check(err)
llgen.Verbose = false
llgenDir(dir + "/cl/_testrt")
llgenDir(dir+"/cl/_testdata", "")
}
func llgenDir(dir string, pkgPath ...string) {
fis, err := os.ReadDir(dir)
check(err)
for _, fi := range fis {
name := fi.Name()
if !fi.IsDir() || strings.HasPrefix(name, "_") {
continue
}
testDir := dir + "/" + name
fmt.Fprintln(os.Stderr, "llgen", testDir)
os.Chdir(testDir)
llgen.SmartDoFile("in.go", pkgPath...)
}
}
func check(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -19,29 +19,16 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"github.com/goplus/llgo/internal/llgen" "github.com/goplus/llgo/internal/llgen"
) )
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "Usage: llgen xxx.go [pkgPath]") fmt.Fprintln(os.Stderr, "Usage: llgen <pkg> [pkgPath]")
return return
} }
inFile := os.Args[1]
dir, _ := filepath.Split(inFile)
outFile := dir + "out.ll"
pkgPath := ""
if len(os.Args) == 3 {
pkgPath = os.Args[2]
} else {
pkgPath = llgen.PkgPath(dir)
}
llgen.Init() llgen.Init()
llgen.Do(pkgPath, inFile, outFile) llgen.SmartDoFile(os.Args[1], os.Args[2:]...)
} }

View File

@@ -3,6 +3,18 @@ source_filename = "apkg"
@"apkg.init$guard" = global ptr null @"apkg.init$guard" = global ptr null
define double @apkg.Max(double %0, double %1) {
_llgo_0:
%2 = fcmp ogt double %0, %1
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
ret double %0
_llgo_2: ; preds = %_llgo_0
ret double %1
}
define void @apkg.init() { define void @apkg.init() {
_llgo_0: _llgo_0:
%0 = load i1, ptr @"apkg.init$guard", align 1 %0 = load i1, ptr @"apkg.init$guard", align 1
@@ -15,15 +27,3 @@ _llgo_1: ; preds = %_llgo_0
_llgo_2: ; preds = %_llgo_1, %_llgo_0 _llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void ret void
} }
define double @apkg.Max(double %0, double %1) {
_llgo_0:
%2 = fcmp ogt double %0, %1
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
ret double %0
_llgo_2: ; preds = %_llgo_0
ret double %1
}

View File

@@ -16,6 +16,14 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void ret void
} }
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call i64 @main.max(i64 1, i64 2)
ret void
}
define i64 @main.max(i64 %0, i64 %1) { define i64 @main.max(i64 %0, i64 %1) {
_llgo_0: _llgo_0:
%2 = icmp sgt i64 %0, %1 %2 = icmp sgt i64 %0, %1
@@ -28,9 +36,4 @@ _llgo_2: ; preds = %_llgo_0
ret i64 %1 ret i64 %1
} }
define void @main() { declare void @"github.com/goplus/llgo/internal/runtime.init"()
_llgo_0:
call void @main.init()
%0 = call i64 @main.max(i64 1, i64 2)
ret void
}

View File

@@ -1,8 +1,8 @@
; ModuleID = 'main' ; ModuleID = 'main'
source_filename = "main" source_filename = "main"
@"main.init$guard" = global ptr null
@main.hello = global ptr null @main.hello = global ptr null
@"main.init$guard" = global ptr null
define void @main.init() { define void @main.init() {
_llgo_0: _llgo_0:
@@ -27,6 +27,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
define void @main() { define void @main() {
_llgo_0: _llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init() call void @main.init()
%0 = call i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64 2, i64 100) %0 = call i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64 2, i64 100)
call void (ptr, ...) @printf(ptr @main.hello) call void (ptr, ...) @printf(ptr @main.hello)
@@ -35,6 +36,8 @@ _llgo_0:
declare void @"github.com/goplus/llgo/cl/internal/stdio.init"() declare void @"github.com/goplus/llgo/cl/internal/stdio.init"()
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64, i64) declare i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64, i64)
declare void @printf(ptr, ...) declare void @printf(ptr, ...)

19
cl/_testdata/method/in.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import _ "unsafe"
type T int
func (a T) Add(b T) T {
return a + b
}
//go:linkname printf C.printf
func printf(format *int8, __llgo_va_list ...any)
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
func main() {
a := T(1)
printf(&format[0], a.Add(2))
}

View File

@@ -0,0 +1,54 @@
; ModuleID = 'main'
source_filename = "main"
@main.format = global ptr null
@"main.init$guard" = global ptr null
define i64 @"(main.T).Add"(i64 %0, i64 %1) {
_llgo_0:
%2 = add i64 %0, %1
ret i64 %2
}
define i64 @"(*main.T).Add"(ptr %0, i64 %1) {
_llgo_0:
%2 = load i64, ptr %0, align 4
%3 = call i64 @"(main.T).Add"(i64 %2, i64 %1)
ret i64 %3
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
store i8 72, ptr @main.format, align 1
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call i64 @"(main.T).Add"(i64 1, i64 2)
call void (ptr, ...) @printf(ptr @main.format, i64 %0)
ret void
}
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -2,7 +2,7 @@ package main
import _ "unsafe" import _ "unsafe"
//go:linkname printf printf //go:linkname printf C.printf
func printf(format *int8, __llgo_va_list ...any) func printf(format *int8, __llgo_va_list ...any)
var hello = [...]int8{'H', 'e', 'l', 'l', 'o', '\n', 0} var hello = [...]int8{'H', 'e', 'l', 'l', 'o', '\n', 0}

View File

@@ -1,8 +1,8 @@
; ModuleID = 'main' ; ModuleID = 'main'
source_filename = "main" source_filename = "main"
@"main.init$guard" = global ptr null
@main.hello = global ptr null @main.hello = global ptr null
@"main.init$guard" = global ptr null
define void @main.init() { define void @main.init() {
_llgo_0: _llgo_0:
@@ -24,11 +24,14 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void ret void
} }
declare void @printf(ptr, ...)
define void @main() { define void @main() {
_llgo_0: _llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init() call void @main.init()
call void (ptr, ...) @printf(ptr @main.hello) call void (ptr, ...) @printf(ptr @main.hello)
ret void ret void
} }
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -0,0 +1,12 @@
package main
import _ "unsafe"
//go:linkname printf C.printf
func printf(format *int8, __llgo_va_list ...any)
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
func main() {
printf(&format[0], 100)
}

View File

@@ -0,0 +1,40 @@
; ModuleID = 'main'
source_filename = "main"
@main.format = global ptr null
@"main.init$guard" = global ptr null
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
store i8 72, ptr @main.format, align 1
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
call void (ptr, ...) @printf(ptr @main.format, i64 100)
ret void
}
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -0,0 +1,19 @@
package main
import _ "unsafe"
//go:linkname printf C.printf
func printf(format *int8, __llgo_va_list ...any)
type T int8
func (f *T) Print(v int) {
printf((*int8)(f), v)
}
var format = [...]T{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
func main() {
f := &format[0]
f.Print(100)
}

View File

@@ -0,0 +1,46 @@
; ModuleID = 'main'
source_filename = "main"
@main.format = global ptr null
@"main.init$guard" = global ptr null
define void @"(*main.T).Print"(ptr %0, i64 %1) {
_llgo_0:
call void (ptr, ...) @printf(ptr %0, i64 %1)
ret void
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
store i8 72, ptr @main.format, align 1
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
call void @"(*main.T).Print"(ptr @main.format, i64 100)
ret void
}
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -0,0 +1,11 @@
package main
const c = 100
var a float64 = 1
func main() {
if c > 100 {
a = 0
}
}

View File

@@ -0,0 +1,35 @@
; ModuleID = 'main'
source_filename = "main"
@main.a = global ptr null
@"main.init$guard" = global ptr null
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
store double 1.000000e+00, ptr @main.a, align 8
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()
br i1 false, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
store double 0.000000e+00, ptr @main.a, align 8
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -1,8 +1,8 @@
; ModuleID = 'main' ; ModuleID = 'main'
source_filename = "main" source_filename = "main"
@"main.init$guard" = global ptr null
@main.a = global ptr null @main.a = global ptr null
@"main.init$guard" = global ptr null
define void @main.init() { define void @main.init() {
_llgo_0: _llgo_0:
@@ -20,6 +20,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
define void @main() { define void @main() {
_llgo_0: _llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init() call void @main.init()
%0 = load i64, ptr @main.a, align 4 %0 = load i64, ptr @main.a, align 4
%1 = add i64 %0, 1 %1 = add i64 %0, 1
@@ -27,3 +28,5 @@ _llgo_0:
%2 = load i64, ptr @main.a, align 4 %2 = load i64, ptr @main.a, align 4
ret void ret void
} }
declare void @"github.com/goplus/llgo/internal/runtime.init"()

12
cl/_testrt/alloca/in.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func main() {
s := c.Str("Hi\n")
s2 := c.Alloca(4)
c.Memcpy(s2, c.Pointer(s), 4)
c.Printf(c.Str("%s"), s2)
}

35
cl/_testrt/alloca/out.ll Normal file
View File

@@ -0,0 +1,35 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [4 x i8] c"Hi\0A\00", align 1
@1 = private unnamed_addr constant [3 x i8] c"%s\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 i8, i64 4, align 1
%1 = call ptr @memcpy(ptr %0, ptr @0, i64 4)
%2 = call i32 (ptr, ...) @printf(ptr @1, ptr %0)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @memcpy(ptr, ptr, i64)
declare i32 @printf(ptr, ...)

13
cl/_testrt/allocstr/in.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func hello() string {
return "Hello world\n"
}
func main() {
c.Printf(c.AllocaCStr(hello()))
}

View File

@@ -0,0 +1,50 @@
; 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 [13 x i8] c"Hello world\0A\00", align 1
define %"github.com/goplus/llgo/internal/runtime.String" @main.hello() {
_llgo_0:
%0 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 12)
ret %"github.com/goplus/llgo/internal/runtime.String" %0
}
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 %"github.com/goplus/llgo/internal/runtime.String" @main.hello()
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %0)
%2 = add i64 %1, 1
%3 = alloca i8, i64 %2, align 1
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %3, %"github.com/goplus/llgo/internal/runtime.String" %0)
%5 = call i32 (ptr, ...) @printf(ptr %4)
ret void
}
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 i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String")
declare i32 @printf(ptr, ...)

13
cl/_testrt/any/in.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func incVal(a any) int {
return a.(int) + 1
}
func main() {
c.Printf(c.Str("Hello %d\n"), incVal(100))
}

49
cl/_testrt/any/out.ll Normal file
View File

@@ -0,0 +1,49 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1
define i64 @main.incVal(%"github.com/goplus/llgo/internal/runtime.iface" %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2)
%2 = call i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1)
%3 = add i64 %2, 1
ret i64 %3
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
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.Basic"(i64 2)
%1 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %0, i64 100)
%2 = call i64 @main.incVal(%"github.com/goplus/llgo/internal/runtime.iface" %1)
%3 = call i32 (ptr, ...) @printf(ptr @0, i64 %2)
ret void
}
declare i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface", ptr)
declare ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64)
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr, i64)
declare i32 @printf(ptr, ...)

11
cl/_testrt/builtin/in.go Normal file
View File

@@ -0,0 +1,11 @@
package main
var a int64 = 1<<63 - 1
var b int64 = -1 << 63
var c uint64 = 1<<64 - 1
func main() {
var a = []int{1, 2, 3, 4}
_ = len(a)
_ = len([]int{1, 2, 3, 4})
}

62
cl/_testrt/builtin/out.ll Normal file
View File

@@ -0,0 +1,62 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
@main.a = global ptr null
@main.b = global ptr null
@main.c = global ptr null
@"main.init$guard" = global ptr null
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
store i64 9223372036854775807, ptr @main.a, align 4
store i64 -9223372036854775808, ptr @main.b, align 4
store i64 -1, ptr @main.c, 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 ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
%1 = getelementptr inbounds i64, ptr %0, i64 0
store i64 1, ptr %1, align 4
%2 = getelementptr inbounds i64, ptr %0, i64 1
store i64 2, ptr %2, align 4
%3 = getelementptr inbounds i64, ptr %0, i64 2
store i64 3, ptr %3, align 4
%4 = getelementptr inbounds i64, ptr %0, i64 3
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)
%6 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
%8 = getelementptr inbounds i64, ptr %7, i64 0
store i64 1, ptr %8, align 4
%9 = getelementptr inbounds i64, ptr %7, i64 1
store i64 2, ptr %9, align 4
%10 = getelementptr inbounds i64, ptr %7, i64 2
store i64 3, ptr %10, align 4
%11 = getelementptr inbounds i64, ptr %7, i64 3
store i64 4, ptr %11, align 4
%12 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %7, i64 4, i64 4)
%13 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %12)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(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")

13
cl/_testrt/cstr/in.go Normal file
View File

@@ -0,0 +1,13 @@
package main
import _ "unsafe"
//go:linkname cstr llgo.cstr
func cstr(string) *int8
//go:linkname printf C.printf
func printf(format *int8, __llgo_va_list ...any)
func main() {
printf(cstr("Hello, world\n"))
}

30
cl/_testrt/cstr/out.ll Normal file
View File

@@ -0,0 +1,30 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [14 x i8] c"Hello, world\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 (ptr, ...) @printf(ptr @0)
ret void
}
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

32
cl/_testrt/gblarray/in.go Normal file
View File

@@ -0,0 +1,32 @@
package main
import (
"github.com/goplus/llgo/internal/abi"
"github.com/goplus/llgo/internal/runtime/c"
)
func Basic(kind abi.Kind) *abi.Type {
return basicTypes[kind]
}
var (
basicTypes = [...]*abi.Type{
abi.String: basicType(abi.String),
}
sizeBasicTypes = [...]uintptr{
abi.String: 16,
}
)
func basicType(kind abi.Kind) *abi.Type {
return &abi.Type{
Size_: sizeBasicTypes[kind],
Hash: uint32(kind),
Kind_: uint8(kind),
}
}
func main() {
t := Basic(abi.String)
c.Printf(c.Str("Kind: %d, Size: %d\n"), int(t.Kind_), t.Size_)
}

View File

@@ -0,0 +1,71 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
@main.basicTypes = global ptr null
@"main.init$guard" = global ptr null
@main.sizeBasicTypes = global ptr null
@0 = private unnamed_addr constant [20 x i8] c"Kind: %d, Size: %d\0A\00", align 1
define ptr @main.Basic(i64 %0) {
_llgo_0:
%1 = getelementptr inbounds ptr, ptr @main.basicTypes, i64 %0
%2 = load ptr, ptr %1, align 8
ret ptr %2
}
define ptr @main.basicType(i64 %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 48)
%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
%4 = load i64, ptr %3, align 4
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 2
%6 = trunc i64 %0 to i32
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 6
%8 = trunc i64 %0 to i8
store i64 %4, ptr %2, align 4
store i32 %6, ptr %5, align 4
store i8 %8, ptr %7, align 1
ret ptr %1
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
call void @"github.com/goplus/llgo/internal/abi.init"()
store i64 16, ptr getelementptr inbounds (i64, ptr @main.sizeBasicTypes, i64 24), align 4
%1 = call ptr @main.basicType(i64 24)
store ptr %1, ptr getelementptr inbounds (ptr, ptr @main.basicTypes, i64 24), align 8
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 @main.Basic(i64 24)
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %0, i32 0, i32 6
%2 = load i8, ptr %1, align 1
%3 = sext i8 %2 to i64
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %0, i32 0, i32 0
%5 = load i64, ptr %4, align 4
%6 = call i32 (ptr, ...) @printf(ptr @0, i64 %3, i64 %5)
ret void
}
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64)
declare void @"github.com/goplus/llgo/internal/abi.init"()
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i32 @printf(ptr, ...)

10
cl/_testrt/hello/in.go Normal file
View File

@@ -0,0 +1,10 @@
package main
import "github.com/goplus/llgo/cl/internal/libc"
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
func main() {
sfmt := &format[0]
libc.Printf(sfmt, libc.Strlen(sfmt))
}

43
cl/_testrt/hello/out.ll Normal file
View File

@@ -0,0 +1,43 @@
; ModuleID = 'main'
source_filename = "main"
@main.format = global ptr null
@"main.init$guard" = global ptr null
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
store i8 72, ptr @main.format, align 1
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call i32 @strlen(ptr @main.format)
call void (ptr, ...) @printf(ptr @main.format, i32 %0)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i32 @strlen(ptr)
declare void @printf(ptr, ...)

13
cl/_testrt/map/in.go Normal file
View File

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

29
cl/_testrt/map/out.ll Normal file
View File

@@ -0,0 +1,29 @@
; ModuleID = 'main'
source_filename = "main"
@"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 = call ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"()
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"()

5
cl/_testrt/panic/in.go Normal file
View File

@@ -0,0 +1,5 @@
package main
func main() {
panic("panic message")
}

39
cl/_testrt/panic/out.ll Normal file
View File

@@ -0,0 +1,39 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [14 x i8] c"panic message\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 %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 13)
%1 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %0)
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %1)
unreachable
}
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 %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String")
declare void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface")

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

@@ -0,0 +1,17 @@
package main
import "C"
import _ "unsafe"
//go:linkname printf C.printf
func printf(format *int8, __llgo_va_list ...any)
//go:linkname strlen C.strlen
func strlen(str *int8) C.int
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
func main() {
sfmt := &format[0]
printf(sfmt, strlen(sfmt))
}

43
cl/_testrt/strlen/out.ll Normal file
View File

@@ -0,0 +1,43 @@
; ModuleID = 'main'
source_filename = "main"
@main.format = global ptr null
@"main.init$guard" = global ptr null
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
store i8 72, ptr @main.format, align 1
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call i32 @strlen(ptr @main.format)
call void (ptr, ...) @printf(ptr @main.format, i32 %0)
ret void
}
declare void @printf(ptr, ...)
declare i32 @strlen(ptr)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

25
cl/_testrt/struct/in.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import "C"
import _ "unsafe"
//go:linkname printf C.printf
func printf(format *int8, __llgo_va_list ...any)
type Foo struct {
A C.int
ok bool
}
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
func (p Foo) Print() {
if p.ok {
printf(&format[0], p.A)
}
}
func main() {
foo := Foo{100, true}
foo.Print()
}

73
cl/_testrt/struct/out.ll Normal file
View File

@@ -0,0 +1,73 @@
; ModuleID = 'main'
source_filename = "main"
%main.Foo = type { i32, i1 }
@main.format = global ptr null
@"main.init$guard" = global ptr null
define void @"(main.Foo).Print"(%main.Foo %0) {
_llgo_0:
%1 = alloca %main.Foo, align 8
store %main.Foo %0, ptr %1, align 4
%2 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 1
%3 = load i1, ptr %2, align 1
br i1 %3, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
%4 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 0
%5 = load i32, ptr %4, align 4
call void (ptr, ...) @printf(ptr @main.format, i32 %5)
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @"(*main.Foo).Print"(ptr %0) {
_llgo_0:
%1 = load %main.Foo, ptr %0, align 4
call void @"(main.Foo).Print"(%main.Foo %1)
ret void
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
store i8 72, ptr @main.format, align 1
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = alloca %main.Foo, align 8
%1 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 0
%2 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 1
store i32 100, ptr %1, align 4
store i1 true, ptr %2, align 1
%3 = load %main.Foo, ptr %0, align 4
call void @"(main.Foo).Print"(%main.Foo %3)
ret void
}
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

16
cl/_testrt/sum/in.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func sum(args ...int) (ret int) {
for _, v := range args {
ret += v
}
return
}
func main() {
c.Printf(c.Str("Hello %d\n"), sum(1, 2, 3, 4))
}

74
cl/_testrt/sum/out.ll Normal file
View File

@@ -0,0 +1,74 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
@"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() {
_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.Alloc"(i64 32)
%1 = getelementptr inbounds i64, ptr %0, i64 0
store i64 1, ptr %1, align 4
%2 = getelementptr inbounds i64, ptr %0, i64 1
store i64 2, ptr %2, align 4
%3 = getelementptr inbounds i64, ptr %0, i64 2
store i64 3, ptr %3, align 4
%4 = getelementptr inbounds i64, ptr %0, i64 3
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)
%6 = call i64 @main.sum(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%7 = call i32 (ptr, ...) @printf(ptr @0, i64 %6)
ret void
}
define i64 @main.sum(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
br label %_llgo_1
_llgo_1: ; preds = %_llgo_2, %_llgo_0
%2 = phi i64 [ 0, %_llgo_0 ], [ %9, %_llgo_2 ]
%3 = phi i64 [ -1, %_llgo_0 ], [ %4, %_llgo_2 ]
%4 = add i64 %3, 1
%5 = icmp slt i64 %4, %1
br i1 %5, label %_llgo_2, label %_llgo_3
_llgo_2: ; preds = %_llgo_1
%6 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
%7 = getelementptr inbounds i64, ptr %6, i64 %4
%8 = load i64, ptr %7, align 4
%9 = add i64 %2, %8
br label %_llgo_1
_llgo_3: ; preds = %_llgo_1
ret i64 %2
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64)
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr, i64, i64)
declare i32 @printf(ptr, ...)
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")

25
cl/_testrt/typalias/in.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import "C"
import _ "unsafe"
//go:linkname printf C.printf
func printf(format *int8, __llgo_va_list ...any)
type Foo = struct {
A C.int
ok bool
}
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
func Print(p *Foo) {
if p.ok {
printf(&format[0], p.A)
}
}
func main() {
foo := &Foo{100, true}
Print(foo)
}

View File

@@ -0,0 +1,63 @@
; ModuleID = 'main'
source_filename = "main"
@main.format = global ptr null
@"main.init$guard" = global ptr null
define void @main.Print(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 1
%2 = load i1, ptr %1, align 1
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
%3 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 0
%4 = load i32, ptr %3, align 4
call void (ptr, ...) @printf(ptr @main.format, i32 %4)
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
store i8 72, ptr @main.format, align 1
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 5)
%1 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 0
%2 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 1
store i32 100, ptr %1, align 4
store i1 true, ptr %2, align 1
call void @main.Print(ptr %0)
ret void
}
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64)

View File

@@ -0,0 +1,14 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func foo() {
c.Unreachable()
}
func main() {
foo()
c.Printf(c.Str("Hello\n"))
}

View File

@@ -0,0 +1,37 @@
; 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
define void @main.foo() {
_llgo_0:
unreachable
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()
%0 = call i32 (ptr, ...) @printf(ptr @0)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i32 @printf(ptr, ...)

144
cl/builtin_test.go Normal file
View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cl
import (
"go/constant"
"go/types"
"testing"
llssa "github.com/goplus/llgo/ssa"
"golang.org/x/tools/go/ssa"
)
func TestErrAlloca(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("alloca: no error?")
}
}()
var ctz context
ctz.alloca(nil, nil)
}
func TestCStrNoArgs(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("cstr: no error?")
}
}()
cstr(nil, nil)
}
func TestCStrNonconst(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("cstr: no error?")
}
}()
cstr(nil, []ssa.Value{&ssa.Parameter{}})
}
func TestPkgNoInit(t *testing.T) {
pkg := types.NewPackage("foo", "foo")
ctx := &context{
goTyps: pkg,
loaded: make(map[*types.Package]*pkgInfo),
}
if ctx.pkgNoInit(pkg) {
t.Fatal("pkgNoInit?")
}
}
func TestPkgKind(t *testing.T) {
if v := pkgKind("noinit"); v != PkgNoInit {
t.Fatal("pkgKind:", v)
}
if v := pkgKind(""); v != PkgLLGo {
t.Fatal("pkgKind:", v)
}
}
func TestPkgKindOf(t *testing.T) {
if v := PkgKindOf(types.Unsafe); v != PkgDeclOnly {
t.Fatal("PkgKindOf unsafe:", v)
}
pkg := types.NewPackage("foo", "foo")
pkg.Scope().Insert(
types.NewConst(
0, pkg, "LLGoPackage", types.Typ[types.String],
constant.MakeString("noinit")),
)
if v := PkgKindOf(pkg); v != PkgNoInit {
t.Fatal("PkgKindOf foo:", v)
}
}
func TestIsAny(t *testing.T) {
if isAny(types.Typ[types.UntypedInt]) {
t.Fatal("isAny?")
}
}
func TestIntVal(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("intVal: no error?")
}
}()
intVal(&ssa.Parameter{})
}
func TestIgnoreName(t *testing.T) {
if !ignoreName("runtime.foo") || !ignoreName("runtime/foo") || !ignoreName("internal/abi") {
t.Fatal("ignoreName failed")
}
}
func TestErrImport(t *testing.T) {
var ctx context
pkg := types.NewPackage("foo", "foo")
ctx.importPkg(pkg, nil)
}
func TestErrInitLinkname(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("initLinkname: no error?")
}
}()
var ctx context
ctx.initLinkname("foo", "//go:linkname Printf printf")
}
func TestErrVarOf(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("varOf: no error?")
}
}()
prog := llssa.NewProgram(nil)
pkg := prog.NewPackage("foo", "foo")
pkgTypes := types.NewPackage("foo", "foo")
ctx := &context{
pkg: pkg,
goTyps: pkgTypes,
}
ssaPkg := &ssa.Package{Pkg: pkgTypes}
g := &ssa.Global{Pkg: ssaPkg}
ctx.varOf(g)
}

131
cl/cltest/cltest.go Normal file
View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cltest
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
"log"
"os"
"path"
"strings"
"testing"
"github.com/goplus/gogen/packages"
"github.com/goplus/llgo/cl"
"github.com/goplus/llgo/internal/llgen"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
llssa "github.com/goplus/llgo/ssa"
)
func init() {
cl.SetDebug(cl.DbgFlagAll)
llssa.Initialize(llssa.InitAll)
llssa.SetDebug(llssa.DbgFlagAll)
}
func FromDir(t *testing.T, sel, relDir string, byLLGen bool) {
dir, err := os.Getwd()
if err != nil {
t.Fatal("Getwd failed:", err)
}
dir = path.Join(dir, relDir)
fis, err := os.ReadDir(dir)
if err != nil {
t.Fatal("ReadDir failed:", err)
}
for _, fi := range fis {
name := fi.Name()
if !fi.IsDir() || strings.HasPrefix(name, "_") {
continue
}
t.Run(name, func(t *testing.T) {
testFrom(t, dir+"/"+name, sel, byLLGen)
})
}
}
func Pkg(t *testing.T, pkgPath, outFile string) {
b, err := os.ReadFile(outFile)
if err != nil {
t.Fatal("ReadFile failed:", err)
}
expected := string(b)
if v := llgen.GenFrom(pkgPath); v != expected {
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
}
}
func testFrom(t *testing.T, pkgDir, sel string, byLLGen bool) {
if sel != "" && !strings.Contains(pkgDir, sel) {
return
}
log.Println("Parsing", pkgDir)
in := pkgDir + "/in.go"
out := pkgDir + "/out.ll"
b, err := os.ReadFile(out)
if err != nil {
t.Fatal("ReadFile failed:", err)
}
expected := string(b)
if byLLGen {
if v := llgen.GenFrom(in); v != expected {
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
}
} else {
TestCompileEx(t, nil, in, expected)
}
}
func TestCompileEx(t *testing.T, src any, fname, expected string) {
t.Helper()
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fname, src, parser.ParseComments)
if err != nil {
t.Fatal("ParseFile failed:", err)
}
files := []*ast.File{f}
name := f.Name.Name
pkg := types.NewPackage(name, name)
imp := packages.NewImporter(fset)
foo, _, err := ssautil.BuildPackage(
&types.Config{Importer: imp}, fset, pkg, files, ssa.SanityCheckFunctions)
if err != nil {
t.Fatal("BuildPackage failed:", err)
}
foo.WriteTo(os.Stderr)
prog := llssa.NewProgram(nil)
prog.SetRuntime(func() *types.Package {
rt, err := imp.Import(llssa.PkgRuntime)
if err != nil {
t.Fatal("load runtime failed:", err)
}
return rt
})
ret, err := cl.NewPackage(prog, foo, files)
if err != nil {
t.Fatal("cl.NewPackage failed:", err)
}
if v := ret.String(); v != expected {
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
}
}

View File

@@ -19,11 +19,13 @@ package cl
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/constant"
"go/token" "go/token"
"go/types" "go/types"
"log" "log"
"os" "os"
"sort" "sort"
"strings"
llssa "github.com/goplus/llgo/ssa" llssa "github.com/goplus/llgo/ssa"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
@@ -56,16 +58,16 @@ func SetDebug(dbgFlags dbgFlags) {
const ( const (
fnNormal = iota fnNormal = iota
fnHasVArg fnHasVArg
fnUnsafeInit fnIgnore
) )
func funcKind(vfn ssa.Value) int { func (p *context) funcKind(vfn ssa.Value) int {
if fn, ok := vfn.(*ssa.Function); ok && fn.Signature.Recv() == nil { if fn, ok := vfn.(*ssa.Function); ok && fn.Signature.Recv() == nil {
params := fn.Signature.Params() params := fn.Signature.Params()
n := params.Len() n := params.Len()
if n == 0 { if n == 0 {
if fn.Name() == "init" && fn.Pkg.Pkg.Path() == "unsafe" { if fn.Name() == "init" && p.pkgNoInit(fn.Pkg.Pkg) {
return fnUnsafeInit return fnIgnore
} }
} else { } else {
last := params.At(n - 1) last := params.At(n - 1)
@@ -77,36 +79,108 @@ func funcKind(vfn ssa.Value) int {
return fnNormal return fnNormal
} }
func (p *context) pkgNoInit(pkg *types.Package) bool {
p.ensureLoaded(pkg)
if i, ok := p.loaded[pkg]; ok {
return i.kind >= PkgNoInit
}
return false
}
func ignoreName(name string) bool {
/* TODO(xsw): confirm this is not needed more
if name == "unsafe.init" {
return true
}
*/
if strings.HasPrefix(name, "internal/") || strings.HasPrefix(name, "crypto/") ||
strings.HasPrefix(name, "arena.") || strings.HasPrefix(name, "maps.") ||
strings.HasPrefix(name, "time.") || strings.HasPrefix(name, "syscall.") ||
strings.HasPrefix(name, "os.") || strings.HasPrefix(name, "plugin.") ||
strings.HasPrefix(name, "reflect.") || strings.HasPrefix(name, "errors.") {
return true // TODO(xsw)
}
return inPkg(name, "runtime") || inPkg(name, "sync")
}
func inPkg(name, pkg string) bool {
if len(name) > len(pkg) && strings.HasPrefix(name, pkg) {
c := name[len(pkg)]
return c == '.' || c == '/'
}
return false
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type none = struct{} type instrOrValue interface {
type instrAndValue interface {
ssa.Instruction ssa.Instruction
ssa.Value ssa.Value
} }
const (
PkgNormal = iota
PkgLLGo
PkgNoInit // noinit: a package that don't need to be initialized
PkgDeclOnly // decl: a package that only have declarations
)
type pkgInfo struct {
kind int
}
type context struct { type context struct {
prog llssa.Program prog llssa.Program
pkg llssa.Package pkg llssa.Package
fn llssa.Function fn llssa.Function
fset *token.FileSet fset *token.FileSet
goProg *ssa.Program
goTyps *types.Package goTyps *types.Package
goPkg *ssa.Package goPkg *ssa.Package
link map[string]string // pkgPath.nameInPkg => linkname link map[string]string // pkgPath.nameInPkg => linkname
loaded map[*types.Package]none // loaded packages loaded map[*types.Package]*pkgInfo // loaded packages
bvals map[ssa.Value]llssa.Expr // block values bvals map[ssa.Value]llssa.Expr // block values
vargs map[*ssa.Alloc][]llssa.Expr // varargs
inits []func() inits []func()
phis []func()
} }
func (p *context) compileType(pkg llssa.Package, member *ssa.Type) { func (p *context) compileType(pkg llssa.Package, t *ssa.Type) {
panic("todo") tn := t.Object().(*types.TypeName)
if tn.IsAlias() { // don't need to compile alias type
return
}
tnName := tn.Name()
typ := tn.Type()
name := llssa.FullName(tn.Pkg(), tnName)
if ignoreName(name) {
return
}
if debugInstr {
log.Println("==> NewType", name, typ)
}
p.compileMethods(pkg, typ)
p.compileMethods(pkg, types.NewPointer(typ))
}
func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
prog := p.goProg
mthds := prog.MethodSets.MethodSet(typ)
for i, n := 0, mthds.Len(); i < n; i++ {
mthd := mthds.At(i)
if ssaMthd := prog.MethodValue(mthd); ssaMthd != nil {
p.compileFunc(pkg, mthd.Obj().Pkg(), ssaMthd)
}
}
} }
// Global variable. // 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 := fullName(gbl.Pkg.Pkg, gbl.Name()) name := llssa.FullName(gbl.Pkg.Pkg, gbl.Name())
if ignoreName(name) || checkCgo(gbl.Name()) {
return
}
if debugInstr { if debugInstr {
log.Println("==> NewVar", name, typ) log.Println("==> NewVar", name, typ)
} }
@@ -114,20 +188,23 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
g.Init(p.prog.Null(g.Type)) g.Init(p.prog.Null(g.Type))
} }
func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) { func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) {
name := p.funcName(f.Pkg.Pkg, f) sig := f.Signature
if name == "unsafe.init" { name, ftype := p.funcName(pkgTypes, f, true)
switch ftype {
case ignoredFunc, llgoInstr: // llgo extended instructions
return return
} }
if debugInstr { if debugInstr {
log.Println("==> NewFunc", name) log.Println("==> NewFunc", name, "type:", sig.Recv(), sig)
} }
fn := pkg.NewFunc(name, f.Signature) fn := pkg.NewFunc(name, sig)
p.inits = append(p.inits, func() { p.inits = append(p.inits, func() {
p.fn = fn p.fn = fn
defer func() { defer func() {
p.fn = nil p.fn = nil
}() }()
p.phis = nil
nblk := len(f.Blocks) nblk := len(f.Blocks)
if nblk == 0 { // external function if nblk == 0 { // external function
return return
@@ -140,19 +217,23 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) {
} }
fn.MakeBlocks(nblk) fn.MakeBlocks(nblk)
b := fn.NewBuilder() b := fn.NewBuilder()
p.bvals = make(map[ssa.Value]llssa.Expr)
for i, block := range f.Blocks { for i, block := range f.Blocks {
p.compileBlock(b, block, i == 0 && name == "main") p.compileBlock(b, block, i == 0 && name == "main")
} }
for _, phi := range p.phis {
phi()
}
}) })
} }
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bool) llssa.BasicBlock { func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bool) llssa.BasicBlock {
ret := p.fn.Block(block.Index) ret := p.fn.Block(block.Index)
b.SetBlock(ret) b.SetBlock(ret)
p.bvals = make(map[ssa.Value]llssa.Expr)
if doInit { if doInit {
fn := p.pkg.FuncOf("main.init") pkg := p.pkg
b.Call(fn.Expr) callRuntimeInit(b, pkg)
b.Call(pkg.FuncOf("main.init").Expr)
} }
for _, instr := range block.Instrs { for _, instr := range block.Instrs {
p.compileInstr(b, instr) p.compileInstr(b, instr)
@@ -160,23 +241,135 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bo
return ret return ret
} }
func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret llssa.Expr) { const (
RuntimeInit = llssa.PkgRuntime + ".init"
)
func callRuntimeInit(b llssa.Builder, pkg llssa.Package) {
fn := pkg.NewFunc(RuntimeInit, types.NewSignatureType(nil, nil, nil, nil, nil, false))
b.Call(fn.Expr)
}
func isAny(t types.Type) bool {
if t, ok := t.(*types.Interface); ok {
return t.Empty()
}
return false
}
func intVal(v ssa.Value) int64 {
if c, ok := v.(*ssa.Const); ok {
if iv, exact := constant.Int64Val(c.Value); exact {
return iv
}
}
panic("intVal: ssa.Value is not a const int")
}
func (p *context) isVArgs(vx ssa.Value) (ret []llssa.Expr, ok bool) {
if va, vok := vx.(*ssa.Alloc); vok {
ret, ok = p.vargs[va] // varargs: this is a varargs index
}
return
}
func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool {
if v.Comment == "varargs" { // this is a varargs allocation
if arr, ok := t.Elem().(*types.Array); ok {
if isAny(arr.Elem()) {
p.vargs[v] = make([]llssa.Expr, arr.Len())
return true
}
}
}
return false
}
// func cstr(string) *int8
func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 1 {
if c, ok := args[0].(*ssa.Const); ok {
if v := c.Value; v.Kind() == constant.String {
sv := constant.StringVal(v)
return b.CStr(sv)
}
}
}
panic("cstr(<string-literal>): invalid arguments")
}
// func alloca(size uintptr) unsafe.Pointer
func (p *context) alloca(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 1 {
n := p.compileValue(b, args[0])
return b.Alloca(n)
}
panic("alloca(size uintptr): invalid arguments")
}
// func allocaCStr(s string) *int8
func (p *context) allocaCStr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 1 {
s := p.compileValue(b, args[0])
return b.AllocaCStr(s)
}
panic("allocaCStr(s string): invalid arguments")
}
func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) {
if asValue {
if v, ok := p.bvals[iv]; ok { if v, ok := p.bvals[iv]; ok {
return v return v
} }
log.Panicln("unreachable:", iv)
}
switch v := iv.(type) { switch v := iv.(type) {
case *ssa.Call: case *ssa.Call:
call := v.Call call := v.Call
kind := funcKind(call.Value) cv := call.Value
if kind == fnUnsafeInit { kind := p.funcKind(cv)
if kind == fnIgnore {
return return
} }
if debugGoSSA { if debugGoSSA {
log.Println(">>> Call", call.Value, call.Args) log.Println(">>> Call", cv, call.Args)
} }
fn := p.compileValue(b, call.Value) switch cv := cv.(type) {
case *ssa.Builtin:
fn := cv.Name()
if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr
arg := call.Args[0]
ret = p.compileValue(b, arg)
// log.Println("wrapnilchk:", ret.TypeOf())
} else {
args := p.compileValues(b, call.Args, kind)
ret = b.BuiltinCall(fn, args...)
}
case *ssa.Function:
fn, ftype := p.funcOf(cv)
switch ftype {
case goFunc, cFunc:
args := p.compileValues(b, call.Args, kind)
ret = b.Call(fn.Expr, args...)
case llgoCstr:
ret = cstr(b, call.Args)
case llgoAlloca:
ret = p.alloca(b, call.Args)
case llgoAllocaCStr:
ret = p.allocaCStr(b, call.Args)
case llgoUnreachable: // func unreachable()
b.Unreachable()
default:
panic("todo")
}
default:
panic("todo")
/*
fn := p.compileValue(b, cv)
args := p.compileValues(b, call.Args, kind) args := p.compileValues(b, call.Args, kind)
ret = b.Call(fn, args...) ret = b.Call(fn, args...)
*/
}
case *ssa.BinOp: case *ssa.BinOp:
x := p.compileValue(b, v.X) x := p.compileValue(b, v.X)
y := p.compileValue(b, v.Y) y := p.compileValue(b, v.Y)
@@ -184,13 +377,77 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l
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.IndexAddr: 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:
t := v.Type()
x := p.compileValue(b, v.X) x := p.compileValue(b, v.X)
ret = b.ChangeType(p.prog.Type(t), x)
case *ssa.Convert:
t := v.Type()
x := p.compileValue(b, v.X)
ret = b.Convert(p.prog.Type(t), x)
case *ssa.FieldAddr:
x := p.compileValue(b, v.X)
ret = b.FieldAddr(x, v.Field)
case *ssa.Alloc:
t := v.Type().(*types.Pointer)
if p.checkVArgs(v, t) { // varargs: this is a varargs allocation
return
}
ret = b.Alloc(t, v.Heap)
case *ssa.IndexAddr:
vx := v.X
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index
return
}
x := p.compileValue(b, vx)
idx := p.compileValue(b, v.Index) idx := p.compileValue(b, v.Index)
ret = b.IndexAddr(x, idx) ret = b.IndexAddr(x, idx)
case *ssa.Alloc: case *ssa.Slice:
vx := v.X
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs slice
return
}
var low, high, max llssa.Expr
x := p.compileValue(b, vx)
if v.Low != nil {
low = p.compileValue(b, v.Low)
}
if v.High != nil {
high = p.compileValue(b, v.High)
}
if v.Max != nil {
max = p.compileValue(b, v.Max)
}
ret = b.Slice(x, low, high, max)
case *ssa.MakeMap:
var nReserve llssa.Expr
t := v.Type() t := v.Type()
ret = b.Alloc(p.prog.Type(t), v.Heap) if v.Reserve != nil {
nReserve = p.compileValue(b, v.Reserve)
}
ret = b.MakeMap(p.prog.Type(t), nReserve)
case *ssa.MakeInterface:
const (
delayExpr = true // varargs: don't need to convert an expr to any
)
t := v.Type()
x := p.compileValue(b, v.X)
ret = b.MakeInterface(t, x, delayExpr)
case *ssa.TypeAssert:
x := p.compileValue(b, v.X)
ret = b.TypeAssert(x, p.prog.Type(v.AssertedType), v.CommaOk)
default: default:
panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv)) panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv))
} }
@@ -199,13 +456,25 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l
} }
func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
if iv, ok := instr.(instrAndValue); ok { if iv, ok := instr.(instrOrValue); ok {
p.compileInstrAndValue(b, iv) p.compileInstrOrValue(b, iv, false)
return return
} }
switch v := instr.(type) { switch v := instr.(type) {
case *ssa.Store: case *ssa.Store:
ptr := p.compileValue(b, v.Addr) va := v.Addr
if va, ok := va.(*ssa.IndexAddr); ok {
if args, ok := p.isVArgs(va.X); ok { // varargs: this is a varargs store
idx := intVal(va.Index)
val := v.Val
if vi, ok := val.(*ssa.MakeInterface); ok {
val = vi.X
}
args[idx] = p.compileValue(b, val)
return
}
}
ptr := p.compileValue(b, va)
val := p.compileValue(b, v.Val) val := p.compileValue(b, v.Val)
b.Store(ptr, val) b.Store(ptr, val)
case *ssa.Jump: case *ssa.Jump:
@@ -229,14 +498,22 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
thenb := fn.Block(succs[0].Index) thenb := fn.Block(succs[0].Index)
elseb := fn.Block(succs[1].Index) elseb := fn.Block(succs[1].Index)
b.If(cond, thenb, elseb) b.If(cond, thenb, elseb)
case *ssa.MapUpdate:
m := p.compileValue(b, v.Map)
key := p.compileValue(b, v.Key)
val := p.compileValue(b, v.Value)
b.MapUpdate(m, key, val)
case *ssa.Panic:
arg := p.compileValue(b, v.X).Do()
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) 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.(instrAndValue); ok { if iv, ok := v.(instrOrValue); ok {
return p.compileInstrAndValue(b, iv) return p.compileInstrOrValue(b, iv, true)
} }
switch v := v.(type) { switch v := v.(type) {
case *ssa.Parameter: case *ssa.Parameter:
@@ -247,13 +524,19 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
} }
} }
case *ssa.Function: case *ssa.Function:
fn := p.funcOf(v) panic("unreachable")
/*
fn, ftype := p.funcOf(v)
if ftype >= llgoInstrBase {
panic("can't use llgo instruction as a value")
}
return fn.Expr return fn.Expr
*/
case *ssa.Global: case *ssa.Global:
g := p.varOf(v) g := p.varOf(v)
return g.Expr return g.Expr
case *ssa.Const: case *ssa.Const:
t := 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))
} }
panic(fmt.Sprintf("compileValue: unknown value - %T\n", v)) panic(fmt.Sprintf("compileValue: unknown value - %T\n", v))
@@ -262,19 +545,23 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
func (p *context) compileVArg(ret []llssa.Expr, b llssa.Builder, v ssa.Value) []llssa.Expr { func (p *context) compileVArg(ret []llssa.Expr, b llssa.Builder, v ssa.Value) []llssa.Expr {
_ = b _ = b
switch v := v.(type) { switch v := v.(type) {
case *ssa.Slice: // varargs: this is a varargs slice
if args, ok := p.isVArgs(v.X); ok {
return append(ret, args...)
}
case *ssa.Const: case *ssa.Const:
if v.Value == nil { if v.Value == nil {
return ret return ret
} }
} }
panic("todo") panic(fmt.Sprintf("compileVArg: unknown value - %T\n", v))
} }
func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int) []llssa.Expr { func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int) []llssa.Expr {
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]) ret[i] = p.compileValue(b, vals[i]).Do()
} }
if hasVArg > 0 { if hasVArg > 0 {
ret = p.compileVArg(ret, b, vals[n]) ret = p.compileVArg(ret, b, vals[n])
@@ -291,31 +578,34 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
val ssa.Member val ssa.Member
} }
// Sort by position, so that the order of the functions in the IR matches members := make([]*namedMember, 0, len(pkg.Members))
// the order of functions in the source file. This is useful for testing,
// for example.
var members []*namedMember
for name, v := range pkg.Members { for name, v := range pkg.Members {
members = append(members, &namedMember{name, v}) members = append(members, &namedMember{name, v})
} }
sort.Slice(members, func(i, j int) bool { sort.Slice(members, func(i, j int) bool {
iPos := members[i].val.Pos() return members[i].name < members[j].name
jPos := members[j].val.Pos()
return iPos < jPos
}) })
pkgProg := pkg.Prog
pkgTypes := pkg.Pkg pkgTypes := pkg.Pkg
pkgName, pkgPath := pkgTypes.Name(), pkgTypes.Path() pkgName, pkgPath := pkgTypes.Name(), llssa.PathOf(pkgTypes)
if pkgPath == llssa.PkgRuntime {
prog.SetRuntime(pkgTypes)
}
ret = prog.NewPackage(pkgName, pkgPath) ret = prog.NewPackage(pkgName, pkgPath)
ctx := &context{ ctx := &context{
prog: prog, prog: prog,
pkg: ret, pkg: ret,
fset: pkg.Prog.Fset, fset: pkgProg.Fset,
goProg: pkgProg,
goTyps: pkgTypes, goTyps: pkgTypes,
goPkg: pkg, goPkg: pkg,
link: make(map[string]string), link: make(map[string]string),
loaded: make(map[*types.Package]none), vargs: make(map[*ssa.Alloc][]llssa.Expr),
loaded: map[*types.Package]*pkgInfo{
types.Unsafe: {kind: PkgDeclOnly}, // TODO(xsw): PkgNoInit or PkgDeclOnly?
},
} }
ctx.initFiles(pkgPath, files) ctx.initFiles(pkgPath, files)
for _, m := range members { for _, m := range members {
@@ -326,7 +616,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
// Do not try to build generic (non-instantiated) functions. // Do not try to build generic (non-instantiated) functions.
continue continue
} }
ctx.compileFunc(ret, member) ctx.compileFunc(ret, member.Pkg.Pkg, member)
case *ssa.Type: case *ssa.Type:
ctx.compileType(ret, member) ctx.compileType(ret, member)
case *ssa.Global: case *ssa.Global:

View File

@@ -14,101 +14,30 @@
* limitations under the License. * limitations under the License.
*/ */
package cl package cl_test
import ( import (
"go/ast"
"go/parser"
"go/token"
"go/types"
"log"
"os"
"path"
"strings"
"testing" "testing"
"github.com/goplus/gogen/packages" "github.com/goplus/llgo/cl/cltest"
"golang.org/x/tools/go/ssa" "github.com/goplus/llgo/ssa"
"golang.org/x/tools/go/ssa/ssautil"
llssa "github.com/goplus/llgo/ssa"
) )
func TestFromTestdata(t *testing.T) {
testFromDir(t, "", "./_testdata")
}
func init() {
SetDebug(DbgFlagAll)
llssa.Initialize(llssa.InitAll)
llssa.SetDebug(llssa.DbgFlagAll)
}
func testFromDir(t *testing.T, sel, relDir string) {
dir, err := os.Getwd()
if err != nil {
t.Fatal("Getwd failed:", err)
}
dir = path.Join(dir, relDir)
fis, err := os.ReadDir(dir)
if err != nil {
t.Fatal("ReadDir failed:", err)
}
for _, fi := range fis {
name := fi.Name()
if !fi.IsDir() || strings.HasPrefix(name, "_") {
continue
}
t.Run(name, func(t *testing.T) {
testFrom(t, dir+"/"+name, sel)
})
}
}
func testFrom(t *testing.T, pkgDir, sel string) {
if sel != "" && !strings.Contains(pkgDir, sel) {
return
}
log.Println("Parsing", pkgDir)
in := pkgDir + "/in.go"
out := pkgDir + "/out.ll"
expected, err := os.ReadFile(out)
if err != nil {
t.Fatal("ReadFile failed:", err)
}
testCompileEx(t, nil, in, string(expected))
}
func testCompileEx(t *testing.T, src any, fname, expected string) {
t.Helper()
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fname, src, parser.ParseComments)
if err != nil {
t.Fatal("ParseFile failed:", err)
}
files := []*ast.File{f}
name := f.Name.Name
pkg := types.NewPackage(name, name)
imp := packages.NewImporter(fset)
foo, _, err := ssautil.BuildPackage(
&types.Config{Importer: imp}, fset, pkg, files, ssa.SanityCheckFunctions)
if err != nil {
t.Fatal("BuildPackage failed:", err)
}
foo.WriteTo(os.Stderr)
prog := llssa.NewProgram(nil)
ret, err := NewPackage(prog, foo, files)
if err != nil {
t.Fatal("cl.NewPackage failed:", err)
}
if v := ret.String(); v != expected {
t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected)
}
}
func testCompile(t *testing.T, src, expected string) { func testCompile(t *testing.T, src, expected string) {
t.Helper() t.Helper()
testCompileEx(t, src, "foo.go", expected) cltest.TestCompileEx(t, src, "foo.go", expected)
}
func TestFromTestrt(t *testing.T) {
cltest.FromDir(t, "", "./_testrt", true)
}
func TestFromTestdata(t *testing.T) {
cltest.FromDir(t, "", "./_testdata", false)
}
func TestRuntime(t *testing.T) {
cltest.Pkg(t, ssa.PkgRuntime, "../internal/runtime/llgo_autogen.ll")
} }
func TestVar(t *testing.T) { func TestVar(t *testing.T) {
@@ -118,8 +47,8 @@ var a int
`, `; ModuleID = 'foo' `, `; ModuleID = 'foo'
source_filename = "foo" source_filename = "foo"
@"foo.init$guard" = global ptr null
@foo.a = global ptr null @foo.a = global ptr null
@"foo.init$guard" = global ptr null
define void @foo.init() { define void @foo.init() {
_llgo_0: _llgo_0:
@@ -147,6 +76,11 @@ source_filename = "foo"
@"foo.init$guard" = global ptr null @"foo.init$guard" = global ptr null
define i64 @foo.fn(i64 %0, double %1) {
_llgo_0:
ret i64 1
}
define void @foo.init() { define void @foo.init() {
_llgo_0: _llgo_0:
%0 = load i1, ptr @"foo.init$guard", align 1 %0 = load i1, ptr @"foo.init$guard", align 1
@@ -159,10 +93,5 @@ _llgo_1: ; preds = %_llgo_0
_llgo_2: ; preds = %_llgo_1, %_llgo_0 _llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void ret void
} }
define i64 @foo.fn(i64 %0, double %1) {
_llgo_0:
ret i64 1
}
`) `)
} }

View File

@@ -19,6 +19,7 @@ package cl
import ( import (
"bytes" "bytes"
"go/ast" "go/ast"
"go/constant"
"go/token" "go/token"
"go/types" "go/types"
"os" "os"
@@ -43,15 +44,49 @@ func contentOf(m contentMap, file string) (lines contentLines, err error) {
return return
} }
func (p *context) importPkg(pkg *types.Package) { // PkgKindOf returns the kind of a package.
func PkgKindOf(pkg *types.Package) int {
scope := pkg.Scope() scope := pkg.Scope()
if scope.Lookup("LLGoPackage") == nil { kind := pkgKindByScope(scope)
if kind == PkgNormal {
kind = pkgKindByPath(pkg.Path())
}
return kind
}
// decl: a package that only contains declarations
// noinit: a package that does not need to be initialized
func pkgKind(v string) int {
switch v {
case "decl":
return PkgDeclOnly
case "noinit":
return PkgNoInit
}
return PkgLLGo
}
func pkgKindByScope(scope *types.Scope) int {
if v, ok := scope.Lookup("LLGoPackage").(*types.Const); ok {
if v := v.Val(); v.Kind() == constant.String {
return pkgKind(constant.StringVal(v))
}
return PkgLLGo
}
return PkgNormal
}
func (p *context) importPkg(pkg *types.Package, i *pkgInfo) {
scope := pkg.Scope()
kind := pkgKindByScope(scope)
if kind == PkgNormal {
return return
} }
i.kind = kind
fset := p.fset fset := p.fset
names := scope.Names() names := scope.Names()
contents := make(contentMap) contents := make(contentMap)
pkgPath := pkg.Path() pkgPath := llssa.PathOf(pkg)
for _, name := range names { for _, name := range names {
if token.IsExported(name) { if token.IsExported(name) {
obj := scope.Lookup(name) obj := scope.Lookup(name)
@@ -98,51 +133,103 @@ func (p *context) initLinkname(pkgPath, line string) {
if strings.HasPrefix(line, linkname) { if strings.HasPrefix(line, linkname) {
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 {
name := pkgPath + "." + text[:idx]
link := strings.TrimLeft(text[idx+1:], " ") link := strings.TrimLeft(text[idx+1:], " ")
if strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr
name := pkgPath + "." + text[:idx]
p.link[name] = link p.link[name] = link
} else {
panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf")
}
} }
} }
} }
func fullName(pkg *types.Package, name string) string { // func: pkg.name
pkgPath := pkg.Name() // method: (pkg.T).name, (*pkg.T).name
if pkgPath != "main" {
pkgPath = pkg.Path()
}
return pkgPath + "." + name
}
func funcName(pkg *types.Package, fn *ssa.Function) string { func funcName(pkg *types.Package, fn *ssa.Function) string {
ret := fullName(pkg, fn.Name()) sig := fn.Signature
name := fn.Name()
if recv := sig.Recv(); recv != nil {
var tName string
t := recv.Type()
if tp, ok := t.(*types.Pointer); ok {
t, tName = tp.Elem(), "*"
}
tName += llssa.NameOf(t.(*types.Named))
return "(" + tName + ")." + name
}
ret := llssa.FullName(pkg, name)
if ret == "main.main" { if ret == "main.main" {
ret = "main" ret = "main"
} }
return ret return ret
} }
func (p *context) funcName(pkg *types.Package, fn *ssa.Function) string { func checkCgo(fnName string) bool {
name := funcName(pkg, fn) return len(fnName) > 4 && fnName[0] == '_' && fnName[2] == 'g' && fnName[3] == 'o' &&
if v, ok := p.link[name]; ok { (fnName[1] == 'C' || fnName[1] == 'c') &&
return v (fnName[4] == '_' || strings.HasPrefix(fnName[4:], "Check"))
}
return name
} }
func (p *context) funcOf(fn *ssa.Function) llssa.Function { const (
ignoredFunc = iota
goFunc
cFunc
llgoInstr = -1
llgoInstrBase = 0x80
llgoUnreachable = llgoInstrBase + 0
llgoCstr = llgoInstrBase + 1
llgoAlloca = llgoInstrBase + 2
llgoAllocaCStr = llgoInstrBase + 3
)
func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, int) {
name := funcName(pkg, fn)
if ignore && ignoreName(name) || checkCgo(fn.Name()) {
return name, ignoredFunc
}
if v, ok := p.link[name]; ok {
if strings.HasPrefix(v, "C.") {
return v[2:], cFunc
}
if strings.HasPrefix(v, "llgo.") {
return v[5:], llgoInstr
}
return v, goFunc
}
return name, goFunc
}
// 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) pkgTypes := p.ensureLoaded(fn.Pkg.Pkg)
pkg := p.pkg pkg := p.pkg
name := p.funcName(pkgTypes, fn) name, ftype := p.funcName(pkgTypes, fn, false)
if ret := pkg.FuncOf(name); ret != nil { if ftype == llgoInstr {
return ret switch name {
case "cstr":
ftype = llgoCstr
case "alloca":
ftype = llgoAlloca
case "allocaCStr":
ftype = llgoAllocaCStr
case "unreachable":
ftype = llgoUnreachable
default:
panic("unknown llgo instruction: " + name)
} }
return pkg.NewFunc(name, fn.Signature) } else if ret = pkg.FuncOf(name); ret == nil {
ret = pkg.NewFunc(name, fn.Signature)
}
return
} }
func (p *context) varOf(v *ssa.Global) llssa.Global { func (p *context) varOf(v *ssa.Global) llssa.Global {
pkgTypes := p.ensureLoaded(v.Pkg.Pkg) pkgTypes := p.ensureLoaded(v.Pkg.Pkg)
pkg := p.pkg pkg := p.pkg
name := fullName(pkgTypes, v.Name()) name := llssa.FullName(pkgTypes, v.Name())
if ret := pkg.VarOf(name); ret != nil { if ret := pkg.VarOf(name); ret != nil {
return ret return ret
} }
@@ -152,9 +239,20 @@ func (p *context) varOf(v *ssa.Global) llssa.Global {
func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package { func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package {
if p.goTyps != pkgTypes { if p.goTyps != pkgTypes {
if _, ok := p.loaded[pkgTypes]; !ok { if _, ok := p.loaded[pkgTypes]; !ok {
p.loaded[pkgTypes] = none{} i := &pkgInfo{
p.importPkg(pkgTypes) kind: pkgKindByPath(pkgTypes.Path()),
}
p.loaded[pkgTypes] = i
p.importPkg(pkgTypes, i)
} }
} }
return pkgTypes return pkgTypes
} }
func pkgKindByPath(pkgPath string) int {
switch pkgPath {
case "syscall", "runtime/cgo", "unsafe":
return PkgDeclOnly
}
return PkgNormal
}

14
cl/internal/libc/libc.go Normal file
View File

@@ -0,0 +1,14 @@
package libc
import "C"
import _ "unsafe"
const (
LLGoPackage = "decl"
)
//go:linkname Printf C.printf
func Printf(format *int8, __llgo_va_list ...any)
//go:linkname Strlen C.strlen
func Strlen(str *int8) C.int

View File

@@ -6,7 +6,7 @@ const (
LLGoPackage = true LLGoPackage = true
) )
//go:linkname Printf printf //go:linkname Printf C.printf
func Printf(format *int8, __llgo_va_list ...any) func Printf(format *int8, __llgo_va_list ...any)
func Max(a, b int) int { func Max(a, b int) int {

View File

@@ -1,62 +0,0 @@
/*
* Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cl
/*
// Define unimplemented intrinsic functions.
//
// Some functions are either normally implemented in Go assembly (like
// sync/atomic functions) or intentionally left undefined to be implemented
// directly in the compiler (like runtime/volatile functions). Either way, look
// for these and implement them if this is the case.
func (b *builder) defineIntrinsicFunction() {
panic("todo")
}
var mathToLLVMMapping = map[string]string{
"math.Ceil": "llvm.ceil.f64",
"math.Exp": "llvm.exp.f64",
"math.Exp2": "llvm.exp2.f64",
"math.Floor": "llvm.floor.f64",
"math.Log": "llvm.log.f64",
"math.Sqrt": "llvm.sqrt.f64",
"math.Trunc": "llvm.trunc.f64",
}
// defineMathOp defines a math function body as a call to a LLVM intrinsic,
// instead of the regular Go implementation. This allows LLVM to reason about
// the math operation and (depending on the architecture) allows it to lower the
// operation to very fast floating point instructions. If this is not possible,
// LLVM will emit a call to a libm function that implements the same operation.
//
// One example of an optimization that LLVM can do is to convert
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
// beneficial on architectures where 64-bit floating point operations are (much)
// more expensive than 32-bit ones.
func (b *builder) defineMathOp() {
panic("todo")
}
// Implement most math/bits functions.
//
// This implements all the functions that operate on bits. It does not yet
// implement the arithmetic functions (like bits.Add), which also have LLVM
// intrinsics.
func (b *builder) defineMathBitsIntrinsic() bool {
panic("todo")
}
*/

View File

@@ -33,8 +33,13 @@ func init() {
} }
func runCmd(cmd *base.Command, args []string) { func runCmd(cmd *base.Command, args []string) {
build.Do(args, &build.Config{ conf := &build.Config{
Mode: build.ModeBuild, Mode: build.ModeBuild,
AppExt: build.DefaultAppExt(), AppExt: build.DefaultAppExt(),
}) }
if len(args) >= 2 && args[0] == "-o" {
conf.OutFile = args[1]
args = args[2:]
}
build.Do(args, conf)
} }

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package clean implements the "llgo clean" command.
package clean
import (
"github.com/goplus/llgo/cmd/internal/base"
"github.com/goplus/llgo/internal/build"
)
// llgo build
var Cmd = &base.Command{
UsageLine: "llgo clean [clean flags] [build flags] [packages]",
Short: "Remove object files and cached files",
}
func init() {
Cmd.Run = runCmd
}
func runCmd(cmd *base.Command, args []string) {
conf := build.NewDefaultConf(0)
build.Clean(args, conf)
}

View File

@@ -20,7 +20,6 @@ package run
import ( import (
"errors" "errors"
"path/filepath" "path/filepath"
"strings"
"github.com/goplus/llgo/cmd/internal/base" "github.com/goplus/llgo/cmd/internal/base"
"github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/build"
@@ -49,7 +48,7 @@ func runCmd(cmd *base.Command, args []string) {
} }
func parseRunArgs(args []string) ([]string, []string, error) { func parseRunArgs(args []string) ([]string, []string, error) {
n := parseArgs(args) n := build.SkipFlagArgs(args)
if n < 0 { if n < 0 {
return nil, nil, errNoProj return nil, nil, errNoProj
} }
@@ -65,15 +64,6 @@ func parseRunArgs(args []string) ([]string, []string, error) {
return args[:n+1], args[n+1:], nil return args[:n+1], args[n+1:], nil
} }
func parseArgs(args []string) int {
for i, arg := range args {
if !strings.HasPrefix(arg, "-") {
return i
}
}
return -1
}
func isGoFile(fname string) bool { func isGoFile(fname string) bool {
return filepath.Ext(fname) == ".go" return filepath.Ext(fname) == ".go"
} }

View File

@@ -26,6 +26,7 @@ import (
"github.com/goplus/llgo/cmd/internal/base" "github.com/goplus/llgo/cmd/internal/base"
"github.com/goplus/llgo/cmd/internal/build" "github.com/goplus/llgo/cmd/internal/build"
"github.com/goplus/llgo/cmd/internal/clean"
"github.com/goplus/llgo/cmd/internal/help" "github.com/goplus/llgo/cmd/internal/help"
"github.com/goplus/llgo/cmd/internal/install" "github.com/goplus/llgo/cmd/internal/install"
"github.com/goplus/llgo/cmd/internal/run" "github.com/goplus/llgo/cmd/internal/run"
@@ -42,6 +43,7 @@ func init() {
build.Cmd, build.Cmd,
install.Cmd, install.Cmd,
run.Cmd, run.Cmd,
clean.Cmd,
} }
} }

2
go.mod
View File

@@ -5,7 +5,7 @@ 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.1 github.com/goplus/llvm v0.7.3
github.com/goplus/mod v0.13.10 github.com/goplus/mod v0.13.10
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

4
go.sum
View File

@@ -2,8 +2,8 @@ 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/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.1 h1:B12Fr/wc3pAsq5PLuac9u9IuKpLRuCufdVAeGDP/MRw= github.com/goplus/llvm v0.7.3 h1:I7UkAO4kzn0Es2iHKRpGU1LjYQ452XwYfsSs1OAAXk8=
github.com/goplus/llvm v0.7.1/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4= github.com/goplus/llvm v0.7.3/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/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE= github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE=

View File

@@ -0,0 +1,636 @@
; ModuleID = 'github.com/goplus/llgo/internal/abi'
source_filename = "github.com/goplus/llgo/internal/abi"
%"github.com/goplus/llgo/internal/abi.ArrayType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, i64 }
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
%"github.com/goplus/llgo/internal/abi.ChanType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, i64 }
%"github.com/goplus/llgo/internal/abi.FuncType" = type { %"github.com/goplus/llgo/internal/abi.Type", i16, i16 }
%"github.com/goplus/llgo/internal/abi.InterfaceType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" }
%"github.com/goplus/llgo/internal/abi.Name" = type { ptr }
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
%"github.com/goplus/llgo/internal/abi.MapType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, ptr, ptr, i8, i8, i16, i32 }
%"github.com/goplus/llgo/internal/abi.PtrType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr }
%"github.com/goplus/llgo/internal/abi.SliceType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr }
%"github.com/goplus/llgo/internal/abi.StructType" = type { %"github.com/goplus/llgo/internal/abi.Type", %"github.com/goplus/llgo/internal/abi.Name", %"github.com/goplus/llgo/internal/runtime.Slice" }
@"github.com/goplus/llgo/internal/abi.init$guard" = global ptr null
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).ArrayType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).Common"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).FuncType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).InterfaceType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
ret ptr %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.ArrayType).Kind"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
ret i64 %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).MapType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ArrayType).StructType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).ArrayType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).Common"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).FuncType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).InterfaceType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
ret ptr %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.ChanType).Kind"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
ret i64 %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.ChanType).Len"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
ret i64 %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).MapType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.ChanType).StructType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).ArrayType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).Common"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).Elem"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Elem"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).FuncType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).InterfaceType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
ret ptr %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.FuncType).Kind"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
ret i64 %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.FuncType).Len"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
ret i64 %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).MapType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.FuncType).StructType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.FuncType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).ArrayType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).Common"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).Elem"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Elem"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).FuncType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).InterfaceType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
ret ptr %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.InterfaceType).Kind"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
ret i64 %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.InterfaceType).Len"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
ret i64 %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).MapType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.InterfaceType).StructType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.InterfaceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).ArrayType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).Common"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).FuncType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).InterfaceType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
ret ptr %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.MapType).Kind"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
ret i64 %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.MapType).Len"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
ret i64 %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).MapType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.MapType).StructType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).ArrayType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).Common"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).FuncType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).InterfaceType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
ret ptr %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.PtrType).Kind"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
ret i64 %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.PtrType).Len"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
ret i64 %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).MapType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.PtrType).StructType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).ArrayType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).Common"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).FuncType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).InterfaceType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
ret ptr %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.SliceType).Kind"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
ret i64 %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.SliceType).Len"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
ret i64 %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).MapType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.SliceType).StructType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).ArrayType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).Common"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).Elem"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).Elem"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).FuncType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).InterfaceType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %1)
ret ptr %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.StructType).Kind"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %1)
ret i64 %2
}
define i64 @"(*github.com/goplus/llgo/internal/abi.StructType).Len"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
%2 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %1)
ret i64 %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).MapType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.StructType).StructType"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.StructType", ptr %0, i32 0, i32 0
%2 = call ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %1)
ret ptr %2
}
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).ArrayType"(ptr %0) {
_llgo_0:
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
%2 = icmp ne i64 %1, 17
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
ret ptr null
_llgo_2: ; preds = %_llgo_0
ret ptr %0
}
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).Common"(ptr %0) {
_llgo_0:
ret ptr %0
}
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).Elem"(ptr %0) {
_llgo_0:
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
%2 = icmp eq i64 %1, 17
br i1 %2, label %_llgo_1, label %_llgo_3
_llgo_1: ; preds = %_llgo_0
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 1
%4 = load ptr, ptr %3, align 8
ret ptr %4
_llgo_2: ; preds = %_llgo_3
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ChanType", ptr %0, i32 0, i32 1
%6 = load ptr, ptr %5, align 8
ret ptr %6
_llgo_3: ; preds = %_llgo_0
%7 = icmp eq i64 %1, 18
br i1 %7, label %_llgo_2, label %_llgo_5
_llgo_4: ; preds = %_llgo_5
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.MapType", ptr %0, i32 0, i32 2
%9 = load ptr, ptr %8, align 8
ret ptr %9
_llgo_5: ; preds = %_llgo_3
%10 = icmp eq i64 %1, 21
br i1 %10, label %_llgo_4, label %_llgo_7
_llgo_6: ; preds = %_llgo_7
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.PtrType", ptr %0, i32 0, i32 1
%12 = load ptr, ptr %11, align 8
ret ptr %12
_llgo_7: ; preds = %_llgo_5
%13 = icmp eq i64 %1, 22
br i1 %13, label %_llgo_6, label %_llgo_9
_llgo_8: ; preds = %_llgo_9
%14 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.SliceType", ptr %0, i32 0, i32 1
%15 = load ptr, ptr %14, align 8
ret ptr %15
_llgo_9: ; preds = %_llgo_7
%16 = icmp eq i64 %1, 23
br i1 %16, label %_llgo_8, label %_llgo_10
_llgo_10: ; preds = %_llgo_9
ret ptr null
}
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).FuncType"(ptr %0) {
_llgo_0:
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
%2 = icmp ne i64 %1, 19
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
ret ptr null
_llgo_2: ; preds = %_llgo_0
ret ptr %0
}
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).InterfaceType"(ptr %0) {
_llgo_0:
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
%2 = icmp ne i64 %1, 20
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
ret ptr null
_llgo_2: ; preds = %_llgo_0
ret ptr %0
}
define i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0) {
_llgo_0:
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %0, i32 0, i32 6
%2 = load i8, ptr %1, align 1
%3 = and i8 %2, 31
%4 = sext i8 %3 to i64
ret i64 %4
}
define i64 @"(*github.com/goplus/llgo/internal/abi.Type).Len"(ptr %0) {
_llgo_0:
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
%2 = icmp eq i64 %1, 17
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.ArrayType", ptr %0, i32 0, i32 3
%4 = load i64, ptr %3, align 4
ret i64 %4
_llgo_2: ; preds = %_llgo_0
ret i64 0
}
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).MapType"(ptr %0) {
_llgo_0:
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
%2 = icmp ne i64 %1, 21
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
ret ptr null
_llgo_2: ; preds = %_llgo_0
ret ptr %0
}
define ptr @"(*github.com/goplus/llgo/internal/abi.Type).StructType"(ptr %0) {
_llgo_0:
%1 = call i64 @"(*github.com/goplus/llgo/internal/abi.Type).Kind"(ptr %0)
%2 = icmp ne i64 %1, 25
br i1 %2, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
ret ptr null
_llgo_2: ; preds = %_llgo_0
ret ptr %0
}
define void @"github.com/goplus/llgo/internal/abi.init"() {
_llgo_0:
%0 = load i1, ptr @"github.com/goplus/llgo/internal/abi.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"github.com/goplus/llgo/internal/abi.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}

14
internal/abi/map.go Normal file
View File

@@ -0,0 +1,14 @@
// Copyright 2023 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 abi
// Map constants common to several packages
// runtime/runtime-gdb.py:MapTypePrinter contains its own copy
const (
MapBucketCountBits = 3 // log2 of number of elements in a bucket.
MapBucketCount = 1 << MapBucketCountBits
MapMaxKeyBytes = 128 // Must fit in a uint8.
MapMaxElemBytes = 128 // Must fit in a uint8.
)

332
internal/abi/type.go Normal file
View File

@@ -0,0 +1,332 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package abi
import (
"unsafe"
)
// -----------------------------------------------------------------------------
// Type is the runtime representation of a Go type.
//
// Type is also referenced implicitly
// (in the form of expressions involving constants and arch.PtrSize)
// in cmd/compile/internal/reflectdata/reflect.go
// and cmd/link/internal/ld/decodesym.go
// (e.g. data[2*arch.PtrSize+4] references the TFlag field)
// unsafe.OffsetOf(Type{}.TFlag) cannot be used directly in those
// places because it varies with cross compilation and experiments.
type Type struct {
Size_ uintptr
PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers
Hash uint32 // hash of type; avoids computation in hash tables
TFlag TFlag // extra type information flags
Align_ uint8 // alignment of variable with this type
FieldAlign_ uint8 // alignment of struct field with this type
Kind_ uint8 // enumeration for C
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
Equal func(unsafe.Pointer, unsafe.Pointer) bool
// GCData stores the GC type data for the garbage collector.
// If the KindGCProg bit is set in kind, GCData is a GC program.
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
GCData *byte
Str NameOff // string form
PtrToThis TypeOff // type for pointer to this type, may be zero
}
func (t *Type) Kind() Kind { return Kind(t.Kind_ & KindMask) }
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Pointer
Slice
String
Struct
UnsafePointer
)
const (
// TODO (khr, drchase) why aren't these in TFlag? Investigate, fix if possible.
KindDirectIface = 1 << 5
KindGCProg = 1 << 6 // Type.gc points to GC program
KindMask = (1 << 5) - 1
)
// TFlag is used by a Type to signal what extra type information is
// available in the memory directly following the Type value.
type TFlag uint8
const (
// TFlagUncommon means that there is a data with a type, UncommonType,
// just beyond the shared-per-type common data. That is, the data
// for struct types will store their UncommonType at one offset, the
// data for interface types will store their UncommonType at a different
// offset. UncommonType is always accessed via a pointer that is computed
// using trust-us-we-are-the-implementors pointer arithmetic.
//
// For example, if t.Kind() == Struct and t.tflag&TFlagUncommon != 0,
// then t has UncommonType data and it can be accessed as:
//
// type structTypeUncommon struct {
// structType
// u UncommonType
// }
// u := &(*structTypeUncommon)(unsafe.Pointer(t)).u
TFlagUncommon TFlag = 1 << 0
// TFlagExtraStar means the name in the str field has an
// extraneous '*' prefix. This is because for most types T in
// a program, the type *T also exists and reusing the str data
// saves binary size.
TFlagExtraStar TFlag = 1 << 1
// TFlagNamed means the type has a name.
TFlagNamed TFlag = 1 << 2
// TFlagRegularMemory means that equal and hash functions can treat
// this type as a single region of t.size bytes.
TFlagRegularMemory TFlag = 1 << 3
)
// NameOff is the offset to a name from moduledata.types. See resolveNameOff in runtime.
type NameOff int32
// TypeOff is the offset to a type from moduledata.types. See resolveTypeOff in runtime.
type TypeOff int32
// -----------------------------------------------------------------------------
// ArrayType represents a fixed array type.
type ArrayType struct {
Type
Elem *Type // array element type
Slice *Type // slice type
Len uintptr
}
type SliceType struct {
Type
Elem *Type // slice element type
}
type MapType struct {
Type
Key *Type
Elem *Type
Bucket *Type // internal type representing a hash bucket
// function for hashing keys (ptr to key, seed) -> hash
Hasher func(unsafe.Pointer, uintptr) uintptr
KeySize uint8 // size of key slot
ValueSize uint8 // size of elem slot
BucketSize uint16 // size of bucket
Flags uint32
}
type PtrType struct {
Type
Elem *Type // pointer element (pointed at) type
}
type ChanDir int
const (
RecvDir ChanDir = 1 << iota // <-chan
SendDir // chan<-
BothDir = RecvDir | SendDir // chan
InvalidDir ChanDir = 0
)
// ChanType represents a channel type
type ChanType struct {
Type
Elem *Type
Dir ChanDir
}
// funcType represents a function type.
//
// A *Type for each in and out parameter is stored in an array that
// directly follows the funcType (and possibly its uncommonType). So
// a function type with one method, one input, and one output is:
//
// struct {
// funcType
// uncommonType
// [2]*rtype // [0] is in, [1] is out
// }
type FuncType struct {
Type
InCount uint16
OutCount uint16 // top bit is set if last input parameter is ...
}
type StructField struct {
Name Name // name is always non-empty
Typ *Type // type of field
Offset uintptr // byte offset of field
}
type StructType struct {
Type
PkgPath Name
Fields []StructField
}
// Name is an encoded type Name with optional extra data.
//
// The first byte is a bit field containing:
//
// 1<<0 the name is exported
// 1<<1 tag data follows the name
// 1<<2 pkgPath nameOff follows the name and tag
// 1<<3 the name is of an embedded (a.k.a. anonymous) field
//
// Following that, there is a varint-encoded length of the name,
// followed by the name itself.
//
// If tag data is present, it also has a varint-encoded length
// followed by the tag itself.
//
// If the import path follows, then 4 bytes at the end of
// the data form a nameOff. The import path is only set for concrete
// methods that are defined in a different package than their type.
//
// If a name starts with "*", then the exported bit represents
// whether the pointed to type is exported.
//
// Note: this encoding must match here and in:
// cmd/compile/internal/reflectdata/reflect.go
// cmd/link/internal/ld/decodesym.go
type Name struct {
Bytes *byte
}
type InterfaceType struct {
Type
PkgPath Name // import path
Methods []Imethod // sorted by hash
}
// Imethod represents a method on an interface type
type Imethod struct {
Name NameOff // name of method
Typ TypeOff // .(*FuncType) underneath
}
func (t *Type) Common() *Type {
return t
}
// Len returns the length of t if t is an array type, otherwise 0
func (t *Type) Len() int {
if t.Kind() == Array {
return int((*ArrayType)(unsafe.Pointer(t)).Len)
}
return 0
}
// Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil.
func (t *Type) Elem() *Type {
switch t.Kind() {
case Array:
tt := (*ArrayType)(unsafe.Pointer(t))
return tt.Elem
case Chan:
tt := (*ChanType)(unsafe.Pointer(t))
return tt.Elem
case Map:
tt := (*MapType)(unsafe.Pointer(t))
return tt.Elem
case Pointer:
tt := (*PtrType)(unsafe.Pointer(t))
return tt.Elem
case Slice:
tt := (*SliceType)(unsafe.Pointer(t))
return tt.Elem
}
return nil
}
// StructType returns t cast to a *StructType, or nil if its tag does not match.
func (t *Type) StructType() *StructType {
if t.Kind() != Struct {
return nil
}
return (*StructType)(unsafe.Pointer(t))
}
// MapType returns t cast to a *MapType, or nil if its tag does not match.
func (t *Type) MapType() *MapType {
if t.Kind() != Map {
return nil
}
return (*MapType)(unsafe.Pointer(t))
}
// ArrayType returns t cast to a *ArrayType, or nil if its tag does not match.
func (t *Type) ArrayType() *ArrayType {
if t.Kind() != Array {
return nil
}
return (*ArrayType)(unsafe.Pointer(t))
}
// FuncType returns t cast to a *FuncType, or nil if its tag does not match.
func (t *Type) FuncType() *FuncType {
if t.Kind() != Func {
return nil
}
return (*FuncType)(unsafe.Pointer(t))
}
// InterfaceType returns t cast to a *InterfaceType, or nil if its tag does not match.
func (t *Type) InterfaceType() *InterfaceType {
if t.Kind() != Interface {
return nil
}
return (*InterfaceType)(unsafe.Pointer(t))
}
// -----------------------------------------------------------------------------

View File

@@ -19,7 +19,7 @@ package build
import ( import (
"fmt" "fmt"
"go/token" "go/token"
"log" "go/types"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@@ -51,7 +51,8 @@ func needLLFile(mode Mode) bool {
type Config struct { type Config struct {
BinPath string BinPath string
AppExt string // ".exe" on Windows, empty on Unix AppExt string // ".exe" on Windows, empty on Unix
RunArgs []string OutFile string // only valid for ModeBuild when len(pkgs) == 1
RunArgs []string // only valid for ModeRun
Mode Mode Mode Mode
} }
@@ -85,9 +86,9 @@ const (
) )
func Do(args []string, conf *Config) { func Do(args []string, conf *Config) {
flags, patterns, verbose := parseArgs(args) flags, patterns, verbose := ParseArgs(args, buildFlags)
cfg := &packages.Config{ cfg := &packages.Config{
Mode: loadSyntax | packages.NeedDeps | packages.NeedExportFile, Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile,
BuildFlags: flags, BuildFlags: flags,
} }
@@ -97,11 +98,18 @@ func Do(args []string, conf *Config) {
initial, err := packages.Load(cfg, patterns...) initial, err := packages.Load(cfg, patterns...)
check(err) check(err)
// Create SSA-form program representation. mode := conf.Mode
ssaProg, pkgs, errPkgs := allPkgs(initial, ssa.SanityCheckFunctions) if len(initial) == 1 && len(initial[0].CompiledGoFiles) > 0 {
ssaProg.Build() if mode == ModeBuild {
for _, errPkg := range errPkgs { mode = ModeInstall
log.Println("cannot build SSA for package", errPkg) }
} else if mode == ModeRun {
if len(initial) > 1 {
fmt.Fprintln(os.Stderr, "cannot run multiple packages")
} else {
fmt.Fprintln(os.Stderr, "no Go files in matched packages")
}
return
} }
llssa.Initialize(llssa.InitAll) llssa.Initialize(llssa.InitAll)
@@ -110,37 +118,109 @@ func Do(args []string, conf *Config) {
cl.SetDebug(cl.DbgFlagAll) cl.SetDebug(cl.DbgFlagAll)
} }
var rt []*packages.Package
prog := llssa.NewProgram(nil) prog := llssa.NewProgram(nil)
mode := conf.Mode prog.SetRuntime(func() *types.Package {
for _, pkg := range pkgs { rt, err = packages.Load(cfg, llssa.PkgRuntime)
buildPkg(prog, pkg, mode) check(err)
} return rt[0].Types
if mode != ModeBuild || len(initial) == 1 {
for _, pkg := range initial {
if pkg.Name == "main" {
linkMainPkg(pkg, conf, mode)
}
}
}
}
func linkMainPkg(pkg *packages.Package, conf *Config, mode Mode) {
pkgPath := pkg.PkgPath
name := path.Base(pkgPath)
app := filepath.Join(conf.BinPath, name+conf.AppExt)
args := make([]string, 2, len(pkg.Imports)+3)
args[0] = "-o"
args[1] = app
packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) {
if p.PkgPath != "unsafe" { // TODO(xsw): remove this special case
args = append(args, p.ExportFile+".ll")
}
}) })
// TODO(xsw): show work pkgs := buildAllPkgs(prog, initial, mode, verbose)
// fmt.Fprintln(os.Stderr, "clang", args)
var runtimeFiles []string
if rt != nil {
runtimeFiles = allLinkFiles(rt)
}
if mode != ModeBuild {
nErr := 0
for _, pkg := range initial {
if pkg.Name == "main" {
nErr += linkMainPkg(pkg, pkgs, runtimeFiles, conf, mode, verbose)
}
}
if nErr > 0 {
os.Exit(nErr)
}
}
}
func setNeedRuntime(pkg *packages.Package) {
pkg.ID = "" // just use pkg.Module to mark it needs runtime
}
func isNeedRuntime(pkg *packages.Package) bool {
return pkg.ID == ""
}
func buildAllPkgs(prog llssa.Program, initial []*packages.Package, mode Mode, verbose bool) (pkgs []*aPackage) {
// Create SSA-form program representation.
ssaProg, pkgs, errPkgs := allPkgs(initial, ssa.SanityCheckFunctions)
ssaProg.Build()
for _, errPkg := range errPkgs {
for _, err := range errPkg.Errors {
fmt.Fprintln(os.Stderr, err)
}
fmt.Fprintln(os.Stderr, "cannot build SSA for package", errPkg)
}
for _, pkg := range pkgs {
buildPkg(prog, pkg, mode, verbose)
if prog.NeedRuntime() {
setNeedRuntime(pkg.Package)
}
}
return
}
func linkMainPkg(pkg *packages.Package, pkgs []*aPackage, runtimeFiles []string, conf *Config, mode Mode, verbose bool) (nErr int) {
pkgPath := pkg.PkgPath
name := path.Base(pkgPath)
app := conf.OutFile
if app == "" {
app = filepath.Join(conf.BinPath, name+conf.AppExt)
}
const N = 3
args := make([]string, N, len(pkg.Imports)+len(runtimeFiles)+(N+1))
args[0] = "-o"
args[1] = app
args[2] = "-Wno-override-module"
needRuntime := false
packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) {
if p.ExportFile != "" && !isRuntimePkg(p.PkgPath) { // skip packages that only contain declarations
args = append(args, p.ExportFile+".ll")
if !needRuntime {
needRuntime = isNeedRuntime(p)
}
}
})
if needRuntime && runtimeFiles != nil {
args = append(args, runtimeFiles...)
} else {
for _, aPkg := range pkgs {
if aPkg.Package == pkg { // make empty runtime.init if no runtime needed
lpkg := aPkg.LPkg
lpkg.FuncOf(cl.RuntimeInit).MakeBody(1).Return()
if needLLFile(mode) {
file := pkg.ExportFile + ".ll"
os.WriteFile(file, []byte(lpkg.String()), 0644)
}
}
}
}
if verbose || mode != ModeRun {
fmt.Fprintln(os.Stderr, "#", pkgPath) fmt.Fprintln(os.Stderr, "#", pkgPath)
}
defer func() {
if e := recover(); e != nil {
nErr = 1
}
}()
// TODO(xsw): show work
if verbose {
fmt.Fprintln(os.Stderr, "clang", args)
}
err := clang.New("").Exec(args...) err := clang.New("").Exec(args...)
check(err) check(err)
@@ -151,15 +231,22 @@ func linkMainPkg(pkg *packages.Package, conf *Config, mode Mode) {
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Run() cmd.Run()
} }
return
} }
func buildPkg(prog llssa.Program, aPkg aPackage, mode Mode) { func buildPkg(prog llssa.Program, aPkg *aPackage, mode Mode, verbose bool) {
pkg := aPkg.Package pkg := aPkg.Package
if cl.PkgKindOf(pkg.Types) == cl.PkgDeclOnly {
// skip packages that only contain declarations
// and set no export file
pkg.ExportFile = ""
return
}
pkgPath := pkg.PkgPath pkgPath := pkg.PkgPath
if mode != ModeRun { if verbose {
fmt.Fprintln(os.Stderr, pkgPath) fmt.Fprintln(os.Stderr, pkgPath)
} }
if pkgPath == "unsafe" { // TODO(xsw): remove this special case if pkgPath == "unsafe" { // TODO(xsw): maybe can remove this special case
return return
} }
ret, err := cl.NewPackage(prog, aPkg.SSA, pkg.Syntax) ret, err := cl.NewPackage(prog, aPkg.SSA, pkg.Syntax)
@@ -168,14 +255,16 @@ func buildPkg(prog llssa.Program, aPkg aPackage, mode Mode) {
file := pkg.ExportFile + ".ll" file := pkg.ExportFile + ".ll"
os.WriteFile(file, []byte(ret.String()), 0644) os.WriteFile(file, []byte(ret.String()), 0644)
} }
aPkg.LPkg = ret
} }
type aPackage struct { type aPackage struct {
*packages.Package *packages.Package
SSA *ssa.Package SSA *ssa.Package
LPkg llssa.Package
} }
func allPkgs(initial []*packages.Package, mode ssa.BuilderMode) (prog *ssa.Program, all []aPackage, errs []*packages.Package) { func allPkgs(initial []*packages.Package, mode ssa.BuilderMode) (prog *ssa.Program, all []*aPackage, errs []*packages.Package) {
var fset *token.FileSet var fset *token.FileSet
if len(initial) > 0 { if len(initial) > 0 {
fset = initial[0].Fset fset = initial[0].Fset
@@ -185,7 +274,7 @@ func allPkgs(initial []*packages.Package, mode ssa.BuilderMode) (prog *ssa.Progr
packages.Visit(initial, nil, func(p *packages.Package) { packages.Visit(initial, nil, func(p *packages.Package) {
if p.Types != nil && !p.IllTyped { if p.Types != nil && !p.IllTyped {
ssaPkg := prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true) ssaPkg := prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true)
all = append(all, aPackage{p, ssaPkg}) all = append(all, &aPackage{p, ssaPkg, nil})
} else { } else {
errs = append(errs, p) errs = append(errs, p)
} }
@@ -193,20 +282,113 @@ func allPkgs(initial []*packages.Package, mode ssa.BuilderMode) (prog *ssa.Progr
return return
} }
func parseArgs(args []string) (flags, patterns []string, verbose bool) { var (
for i, arg := range args { // TODO(xsw): complete build flags
if !strings.HasPrefix(arg, "-") { buildFlags = map[string]bool{
"-C": true, // -C dir: Change to dir before running the command
"-a": false, // -a: force rebuilding of packages that are already up-to-date
"-n": false, // -n: print the commands but do not run them
"-p": true, // -p n: the number of programs to run in parallel
"-race": false, // -race: enable data race detection
"-cover": false, // -cover: enable coverage analysis
"-covermode": true, // -covermode mode: set the mode for coverage analysis
"-v": false, // -v: print the names of packages as they are compiled
"-work": false, // -work: print the name of the temporary work directory and do not delete it when exiting
"-x": false, // -x: print the commands
"-tags": true, // -tags 'tag,list': a space-separated list of build tags to consider satisfied during the build
"-pkgdir": true, // -pkgdir dir: install and load all packages from dir instead of the usual locations
}
)
func ParseArgs(args []string, swflags map[string]bool) (flags, patterns []string, verbose bool) {
n := len(args)
for i := 0; i < n; i++ {
arg := args[i]
if strings.HasPrefix(arg, "-") {
checkFlag(arg, &i, &verbose, swflags)
} else {
flags, patterns = args[:i], args[i:] flags, patterns = args[:i], args[i:]
return return
} }
if arg == "-v" {
verbose = true
}
} }
flags = args flags = args
return return
} }
func SkipFlagArgs(args []string) int {
n := len(args)
for i := 0; i < n; i++ {
arg := args[i]
if strings.HasPrefix(arg, "-") {
checkFlag(arg, &i, nil, buildFlags)
} else {
return i
}
}
return -1
}
func checkFlag(arg string, i *int, verbose *bool, swflags map[string]bool) {
if hasarg, ok := swflags[arg]; ok {
if hasarg {
*i++
} else if verbose != nil && arg == "-v" {
*verbose = true
}
} else {
panic("unknown flag: " + arg)
}
}
func allLinkFiles(rt []*packages.Package) (outFiles []string) {
outFiles = make([]string, 0, len(rt))
root := rootLLGo(rt[0])
packages.Visit(rt, nil, func(p *packages.Package) {
pkgPath := p.PkgPath
if isRuntimePkg(pkgPath) {
outFile := filepath.Join(root+pkgPath[len(llgoModPath):], "llgo_autogen.ll")
outFiles = append(outFiles, outFile)
}
})
return
}
const (
pkgAbi = llgoModPath + "/internal/abi"
pkgRuntime = llgoModPath + "/internal/runtime"
)
func isRuntimePkg(pkgPath string) bool {
switch pkgPath {
case pkgRuntime, pkgAbi:
return true
}
return false
}
// TODO(xsw): llgo root dir
func rootLLGo(runtime *packages.Package) string {
return runtime.Module.Dir
}
const (
llgoModPath = "github.com/goplus/llgo"
)
/*
func isPkgInLLGo(pkgPath string) bool {
return isPkgInMod(pkgPath, llgoModPath)
}
func isPkgInMod(pkgPath, modPath string) bool {
if strings.HasPrefix(pkgPath, modPath) {
suffix := pkgPath[len(modPath):]
return suffix == "" || suffix[0] == '/'
}
return false
}
*/
func check(err error) { func check(err error) {
if err != nil { if err != nil {
panic(err) panic(err)

85
internal/build/clean.go Normal file
View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package build
import (
"fmt"
"os"
"path"
"path/filepath"
"golang.org/x/tools/go/packages"
)
var (
// TODO(xsw): complete clean flags
cleanFlags = map[string]bool{
"-v": false, // -v: print the paths of packages as they are clean
}
)
func Clean(args []string, conf *Config) {
flags, patterns, verbose := ParseArgs(args, cleanFlags)
cfg := &packages.Config{
Mode: loadSyntax | packages.NeedExportFile,
BuildFlags: flags,
}
if patterns == nil {
patterns = []string{"."}
}
initial, err := packages.Load(cfg, patterns...)
check(err)
cleanPkgs(initial, verbose)
for _, pkg := range initial {
if pkg.Name == "main" {
cleanMainPkg(pkg, conf, verbose)
}
}
}
func cleanMainPkg(pkg *packages.Package, conf *Config, verbose bool) {
pkgPath := pkg.PkgPath
name := path.Base(pkgPath)
fname := name + conf.AppExt
app := filepath.Join(conf.BinPath, fname)
removeFile(app, verbose)
if len(pkg.CompiledGoFiles) > 0 {
dir := filepath.Dir(pkg.CompiledGoFiles[0])
buildApp := filepath.Join(dir, fname)
removeFile(buildApp, verbose)
}
}
func cleanPkgs(initial []*packages.Package, verbose bool) {
packages.Visit(initial, nil, func(p *packages.Package) {
file := p.ExportFile + ".ll"
removeFile(file, verbose)
})
}
func removeFile(file string, verbose bool) {
if _, err := os.Stat(file); os.IsNotExist(err) {
return
}
if verbose {
fmt.Fprintln(os.Stderr, "Remove", file)
}
os.Remove(file)
}

View File

@@ -66,7 +66,9 @@ func Gen(pkgPath, inFile string, src any) string {
&types.Config{Importer: imp}, fset, pkg, files, ssa.SanityCheckFunctions) &types.Config{Importer: imp}, fset, pkg, files, ssa.SanityCheckFunctions)
check(err) check(err)
if Verbose {
ssaPkg.WriteTo(os.Stderr) ssaPkg.WriteTo(os.Stderr)
}
prog := llssa.NewProgram(nil) prog := llssa.NewProgram(nil)
ret, err := cl.NewPackage(prog, ssaPkg, files) ret, err := cl.NewPackage(prog, ssaPkg, files)
@@ -80,3 +82,7 @@ func check(err error) {
panic(err) panic(err)
} }
} }
var (
Verbose = true
)

94
internal/llgen/llgenf.go Normal file
View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package llgen
import (
"go/types"
"os"
"path/filepath"
"strings"
"github.com/goplus/llgo/cl"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
llssa "github.com/goplus/llgo/ssa"
)
const (
loadFiles = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles
loadImports = loadFiles | packages.NeedImports
loadTypes = loadImports | packages.NeedTypes | packages.NeedTypesSizes
loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo
)
func GenFrom(fileOrPkg string) string {
cfg := &packages.Config{
Mode: loadSyntax | packages.NeedDeps,
}
initial, err := packages.Load(cfg, fileOrPkg)
check(err)
_, pkgs := ssautil.AllPackages(initial, ssa.SanityCheckFunctions)
pkg := initial[0]
ssaPkg := pkgs[0]
ssaPkg.Build()
prog := llssa.NewProgram(nil)
prog.SetRuntime(func() *types.Package {
rt, err := packages.Load(cfg, llssa.PkgRuntime)
check(err)
return rt[0].Types
})
if Verbose {
ssaPkg.WriteTo(os.Stderr)
}
ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax)
check(err)
return ret.String()
}
func DoFile(fileOrPkg, outFile string) {
ret := GenFrom(fileOrPkg)
err := os.WriteFile(outFile, []byte(ret), 0644)
check(err)
}
func SmartDoFile(inFile string, pkgPath ...string) {
dir, _ := filepath.Split(inFile)
fname := "llgo_autogen.ll"
if inCompilerDir(dir) {
fname = "out.ll"
}
outFile := dir + fname
if len(pkgPath) > 0 {
Do(pkgPath[0], inFile, outFile)
} else {
DoFile(inFile, outFile)
}
}
func inCompilerDir(dir string) bool {
dir, _ = filepath.Abs(dir)
return strings.Contains(filepath.ToSlash(dir), "/llgo/cl/")
}

54
internal/runtime/c/c.go Normal file
View File

@@ -0,0 +1,54 @@
/*
* 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
)
//go:linkname Str llgo.cstr
func Str(string) *Char
//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 Printf C.printf
func Printf(format *Char, __llgo_va_list ...any) Int

View File

@@ -0,0 +1,422 @@
; ModuleID = 'github.com/goplus/llgo/internal/runtime'
source_filename = "github.com/goplus/llgo/internal/runtime"
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
%"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.Slice" = type { ptr, i64, i64 }
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
%"github.com/goplus/llgo/internal/runtime.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.basicTypes" = global ptr null
@"github.com/goplus/llgo/internal/runtime.init$guard" = global ptr null
@"github.com/goplus/llgo/internal/runtime.sizeBasicTypes" = global ptr null
@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
define ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 %0) {
_llgo_0:
%1 = call ptr @malloc(i64 %0)
ret ptr %1
}
define ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 %0) {
_llgo_0:
%1 = getelementptr inbounds ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 %0
%2 = load ptr, ptr %1, align 8
ret ptr %2
}
define ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %0, %"github.com/goplus/llgo/internal/runtime.String" %1) {
_llgo_0:
%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 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1
%4 = load i64, ptr %3, align 4
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0
%6 = load ptr, ptr %5, align 8
%7 = call ptr @memcpy(ptr %0, ptr %6, i64 %4)
%8 = getelementptr inbounds i8, ptr %0, i64 %4
store i8 0, ptr %8, align 1
ret ptr %0
}
define ptr @"github.com/goplus/llgo/internal/runtime.CStrDup"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0:
%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 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 1
%3 = load i64, ptr %2, align 4
%4 = add i64 %3, 1
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 %4)
%6 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %1, align 8
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %5, %"github.com/goplus/llgo/internal/runtime.String" %6)
ret ptr %7
}
define { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
_llgo_0:
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %2, align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
%4 = load ptr, ptr %3, align 8
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
%6 = load ptr, ptr %5, align 8
%7 = icmp eq ptr %6, %1
br i1 %7, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
%9 = load ptr, ptr %8, align 8
%10 = ptrtoint ptr %9 to i64
%mrv = insertvalue { i64, i1 } poison, i64 %10, 0
%mrv1 = insertvalue { i64, i1 } %mrv, i1 true, 1
ret { i64, i1 } %mrv1
_llgo_2: ; preds = %_llgo_0
ret { i64, i1 } zeroinitializer
}
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.EmptyString"() {
_llgo_0:
%0 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 0
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 1
store ptr null, ptr %1, align 8
store i64 0, ptr %2, align 4
%3 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %0, align 8
ret %"github.com/goplus/llgo/internal/runtime.String" %3
}
define i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
_llgo_0:
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %2, align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
%4 = load ptr, ptr %3, align 8
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
%6 = load ptr, ptr %5, align 8
%7 = icmp eq ptr %6, %1
br i1 %7, label %_llgo_1, label %_llgo_2
_llgo_1: ; preds = %_llgo_0
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
%9 = load ptr, ptr %8, align 8
%10 = ptrtoint ptr %9 to i64
ret i64 %10
_llgo_2: ; preds = %_llgo_0
%11 = call %"github.com/goplus/llgo/internal/runtime.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)
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %12)
unreachable
}
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAny"(ptr %0, ptr %1) {
_llgo_0:
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
%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
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 1
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 2
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 4
%8 = getelementptr inbounds i64, ptr %7, i64 0
store ptr %4, ptr %3, align 8
store ptr %0, ptr %5, align 8
store i32 0, ptr %6, align 4
store i64 0, ptr %8, align 4
%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
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 1
store ptr %2, ptr %10, align 8
store ptr %1, ptr %11, align 8
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, align 8
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
}
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %0, i64 %1) {
_llgo_0:
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
%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
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 1
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 2
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 4
%8 = getelementptr inbounds i64, ptr %7, i64 0
store ptr %4, ptr %3, align 8
store ptr %0, ptr %5, align 8
store i32 0, ptr %6, align 4
store i64 0, ptr %8, align 4
%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
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 1
%12 = inttoptr i64 %1 to ptr
store ptr %2, ptr %10, align 8
store ptr %12, ptr %11, align 8
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, align 8
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
}
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %1, align 8
%2 = 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)
%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
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 1
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 2
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 4
%9 = getelementptr inbounds i64, ptr %8, i64 0
store ptr %5, ptr %4, align 8
store ptr %2, ptr %6, align 8
store i32 0, ptr %7, align 4
store i64 0, ptr %9, align 4
%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
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 1
store ptr %3, ptr %11, align 8
store ptr %1, ptr %12, align 8
%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.MakeInterface"(ptr %0, ptr %1, ptr %2) {
_llgo_0:
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
%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
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 2
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 4
%8 = getelementptr inbounds i64, ptr %7, i64 0
store ptr %0, ptr %4, align 8
store ptr %1, ptr %5, align 8
store i32 0, ptr %6, align 4
store i64 0, ptr %8, align 4
%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
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 1
store ptr %3, ptr %10, align 8
store ptr %2, ptr %11, align 8
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, align 8
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
}
define ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"() {
_llgo_0:
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.makemap_small"()
ret ptr %0
}
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 %1, i64 %2) {
_llgo_0:
%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
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 1
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 2
store ptr %0, ptr %4, align 8
store i64 %1, ptr %5, align 4
store i64 %2, ptr %6, align 4
%7 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, align 8
ret %"github.com/goplus/llgo/internal/runtime.Slice" %7
}
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr %0, i64 %1) {
_llgo_0:
%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
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1
store ptr %0, ptr %3, align 8
store i64 %1, ptr %4, align 4
%5 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %2, align 8
ret %"github.com/goplus/llgo/internal/runtime.String" %5
}
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NilSlice"() {
_llgo_0:
%0 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, i32 0, i32 0
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, i32 0, i32 1
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, i32 0, i32 2
store ptr null, ptr %1, align 8
store i64 0, ptr %2, align 4
store i64 0, ptr %3, align 4
%4 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %0, align 8
ret %"github.com/goplus/llgo/internal/runtime.Slice" %4
}
define ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%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 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 0
%3 = load ptr, ptr %2, align 8
ret ptr %3
}
define i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%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 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 1
%3 = load i64, ptr %2, align 4
ret i64 %3
}
define ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0:
%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 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 0
%3 = load ptr, ptr %2, align 8
ret ptr %3
}
define i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%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 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 1
%3 = load i64, ptr %2, align 4
ret i64 %3
}
define void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %0) {
_llgo_0:
%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 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %1, i32 0, i32 0
%3 = load ptr, ptr %2, align 8
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 1
%5 = load ptr, ptr %4, align 8
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %5, i32 0, i32 6
%7 = load i8, ptr %6, align 1
%8 = sext i8 %7 to i64
%9 = icmp eq i64 %8, 24
br i1 %9, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_2, %_llgo_0
ret void
_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 %10, align 8
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %11, i32 0, i32 1
%13 = load i64, ptr %12, align 4
%14 = add i64 %13, 1
%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
}
define ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 48)
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 0
%3 = getelementptr inbounds i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 %0
%4 = load i64, ptr %3, align 4
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 2
%6 = trunc i64 %0 to i32
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 6
%8 = trunc i64 %0 to i8
store i64 %4, ptr %2, align 4
store i32 %6, ptr %5, align 4
store i8 %8, ptr %7, align 1
ret ptr %1
}
declare i32 @rand()
define void @"github.com/goplus/llgo/internal/runtime.init"() {
_llgo_0:
%0 = load i1, ptr @"github.com/goplus/llgo/internal/runtime.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"github.com/goplus/llgo/internal/runtime.init$guard", align 1
call void @"github.com/goplus/llgo/internal/abi.init"()
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 80)
store ptr %1, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
store i64 1, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 1), align 4
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 2), align 4
store i64 1, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 3), align 4
store i64 2, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 4), align 4
store i64 4, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 5), align 4
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 6), align 4
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 7), align 4
store i64 1, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 8), align 4
store i64 2, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 9), align 4
store i64 4, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 10), align 4
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 11), align 4
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 12), align 4
store i64 4, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 13), align 4
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 14), align 4
store i64 8, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 15), align 4
store i64 16, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 16), align 4
store i64 16, ptr getelementptr inbounds (i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 24), align 4
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 1)
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 2)
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 3)
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 4)
%6 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 5)
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 6)
%8 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 7)
%9 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 8)
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 9)
%11 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 10)
%12 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 11)
%13 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 12)
%14 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 13)
%15 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 14)
%16 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 15)
%17 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 16)
%18 = call ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 24)
store ptr %2, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 1), align 8
store ptr %3, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 2), align 8
store ptr %4, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 3), align 8
store ptr %5, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 4), align 8
store ptr %6, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 5), align 8
store ptr %7, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 6), align 8
store ptr %8, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 7), align 8
store ptr %9, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 8), align 8
store ptr %10, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 9), align 8
store ptr %11, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 10), align 8
store ptr %12, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 11), align 8
store ptr %13, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 12), align 8
store ptr %14, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 13), align 8
store ptr %15, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 14), align 8
store ptr %16, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 15), align 8
store ptr %17, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 16), align 8
store ptr %18, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 24), align 8
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define i1 @"github.com/goplus/llgo/internal/runtime.isEmpty"(i8 %0) {
_llgo_0:
%1 = icmp ule i8 %0, 1
ret i1 %1
}
define ptr @"github.com/goplus/llgo/internal/runtime.makemap_small"() {
_llgo_0:
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 48)
%1 = call i32 @rand()
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.hmap", ptr %0, i32 0, i32 4
store i32 %1, ptr %2, align 4
ret ptr %0
}
declare ptr @malloc(i64)
declare ptr @memcpy(ptr, ptr, i64)
declare i32 @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/abi.init"()

1726
internal/runtime/map.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import (
"unsafe"
)
type iface struct {
tab *itab
data unsafe.Pointer
}
/*
type eface struct {
_type *_type
data unsafe.Pointer
}
func efaceOf(ep *any) *eface {
return (*eface)(unsafe.Pointer(ep))
}
*/
// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/reflectdata/reflect.go:/^func.WriteTabs.
type itab struct {
inter *interfacetype
_type *_type
hash uint32 // copy of _type.hash. Used for type switches.
_ [4]byte
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

38
internal/runtime/stubs.go Normal file
View File

@@ -0,0 +1,38 @@
// 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 runtime
import _ "unsafe"
//go:linkname fastrand C.rand
func fastrand() uint32
/* TODO(xsw):
func fastrand() uint32 {
mp := getg().m
// Implement wyrand: https://github.com/wangyi-fudan/wyhash
// Only the platform that math.Mul64 can be lowered
// by the compiler should be in this list.
if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64|
goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le|
goarch.IsS390x|goarch.IsRiscv64|goarch.IsLoong64 == 1 {
mp.fastrand += 0xa0761d6478bd642f
hi, lo := math.Mul64(mp.fastrand, mp.fastrand^0xe7037ed1a0b428db)
return uint32(hi ^ lo)
}
// Implement xorshift64+: 2 32-bit xorshift sequences added together.
// Shift triplet [17,7,16] was calculated as indicated in Marsaglia's
// Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
// This generator passes the SmallCrush suite, part of TestU01 framework:
// http://simul.iro.umontreal.ca/testu01/tu01.html
t := (*[2]uint32)(unsafe.Pointer(&mp.fastrand))
s1, s0 := t[0], t[1]
s1 ^= s1 << 17
s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16
t[0], t[1] = s0, s1
return s0 + s1
}
*/

36
internal/runtime/type.go Normal file
View File

@@ -0,0 +1,36 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Runtime type representation.
package runtime
import (
"github.com/goplus/llgo/internal/abi"
)
// type nameOff = abi.NameOff
// type typeOff = abi.TypeOff
type _type = abi.Type
type interfacetype = abi.InterfaceType
type maptype = abi.MapType
/*
type arraytype = abi.ArrayType
type chantype = abi.ChanType
type slicetype = abi.SliceType
type functype = abi.FuncType
type ptrtype = abi.PtrType
type name = abi.Name
type structtype = abi.StructType
*/

41
internal/runtime/z_c.go Normal file
View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package runtime
import (
"unsafe"
"github.com/goplus/llgo/internal/abi"
"github.com/goplus/llgo/internal/runtime/c"
)
// Alloc allocates memory.
func Alloc(size uintptr) unsafe.Pointer {
return c.Malloc(size)
}
// TracePanic prints panic message.
func TracePanic(v Interface) {
kind := abi.Kind(v.tab._type.Kind_)
switch {
case kind == abi.String:
s := (*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
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package runtime
import (
"unsafe"
"github.com/goplus/llgo/internal/abi"
)
// -----------------------------------------------------------------------------
type InterfaceType = abi.InterfaceType
var (
TyAny = &InterfaceType{}
)
// -----------------------------------------------------------------------------
type Interface = iface
func MakeAnyInt(typ *Type, data uintptr) Interface {
tab := &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}}
return Interface{
tab: tab, data: unsafe.Pointer(data),
}
}
func MakeAnyString(data string) Interface {
typ := basicTypes[abi.String]
tab := &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}}
return Interface{
tab: tab, data: unsafe.Pointer(&data),
}
}
func MakeAny(typ *Type, data unsafe.Pointer) Interface {
tab := &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}}
return Interface{
tab: tab, data: data,
}
}
func MakeInterface(inter *InterfaceType, typ *Type, data unsafe.Pointer) Interface {
tab := &itab{inter: inter, _type: typ, hash: 0, fun: [1]uintptr{0}}
return Interface{
tab: tab, data: data,
}
}
func I2Int(v Interface, t *Type) uintptr {
if v.tab._type == t {
return uintptr(v.data)
}
panic("I2Int: type mismatch")
}
func CheckI2Int(v Interface, t *Type) (uintptr, bool) {
if v.tab._type == t {
return uintptr(v.data), true
}
return 0, false
}
// -----------------------------------------------------------------------------

25
internal/runtime/z_map.go Normal file
View File

@@ -0,0 +1,25 @@
/*
* 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
// Map represents a Go map.
type Map = hmap
// MakeSmallMap creates a new small map.
func MakeSmallMap() *Map {
return makemap_small()
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package runtime
import (
"unsafe"
)
// -----------------------------------------------------------------------------
// Slice is the runtime representation of a slice.
type Slice struct {
data unsafe.Pointer
len int
cap int
}
// NilSlice returns a nil slice.
func NilSlice() Slice {
return Slice{nil, 0, 0}
}
// NewSlice creates a new slice.
func NewSlice(data unsafe.Pointer, len, cap int) Slice {
return Slice{data, len, cap}
}
// SliceLen returns the length of a slice.
func SliceLen(s Slice) int {
return s.len
}
// SliceData returns the data pointer of a slice.
func SliceData(s Slice) unsafe.Pointer {
return s.data
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package runtime
import (
"unsafe"
"github.com/goplus/llgo/internal/runtime/c"
)
// -----------------------------------------------------------------------------
// String is the runtime representation of a string.
// It cannot be used safely or portably and its representation may
// change in a later release.
//
// Unlike reflect.StringHeader, its Data field is sufficient to guarantee the
// data it references will not be garbage collected.
type String struct {
data unsafe.Pointer
len int
}
// EmptyString returns an empty string.
func EmptyString() String {
return String{nil, 0}
}
// NewString creates a new string.
func NewString(data unsafe.Pointer, len int) String {
return String{data, len}
}
// StringLen returns the length of a string.
func StringLen(s Slice) int {
return s.len
}
// StringData returns the data pointer of a string.
func StringData(s String) unsafe.Pointer {
return s.data
}
// -----------------------------------------------------------------------------
// CStrCopy copies a Go string to a C string buffer and returns it.
func CStrCopy(dest unsafe.Pointer, s String) *int8 {
n := s.len
c.Memcpy(dest, s.data, uintptr(n))
arr := (*[1 << 30]int8)(dest)
arr[n] = 0
return (*int8)(dest)
}
func CStrDup(s String) *int8 {
dest := Alloc(uintptr(s.len + 1))
return CStrCopy(dest, s)
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package runtime
import (
"unsafe"
"github.com/goplus/llgo/internal/abi"
)
// -----------------------------------------------------------------------------
type Kind = abi.Kind
type Type = abi.Type
func Basic(kind Kind) *Type {
return basicTypes[kind]
}
var (
basicTypes = [...]*Type{
abi.Bool: basicType(abi.Bool),
abi.Int: basicType(abi.Int),
abi.Int8: basicType(abi.Int8),
abi.Int16: basicType(abi.Int16),
abi.Int32: basicType(abi.Int32),
abi.Int64: basicType(abi.Int64),
abi.Uint: basicType(abi.Uint),
abi.Uint8: basicType(abi.Uint8),
abi.Uint16: basicType(abi.Uint16),
abi.Uint32: basicType(abi.Uint32),
abi.Uint64: basicType(abi.Uint64),
abi.Uintptr: basicType(abi.Uintptr),
abi.Float32: basicType(abi.Float32),
abi.Float64: basicType(abi.Float64),
abi.Complex64: basicType(abi.Complex64),
abi.Complex128: basicType(abi.Complex128),
abi.String: basicType(abi.String),
}
)
var (
sizeBasicTypes = [...]uintptr{
abi.Bool: unsafe.Sizeof(false),
abi.Int: unsafe.Sizeof(0),
abi.Int8: 1,
abi.Int16: 2,
abi.Int32: 4,
abi.Int64: 8,
abi.Uint: unsafe.Sizeof(uint(0)),
abi.Uint8: 1,
abi.Uint16: 2,
abi.Uint32: 4,
abi.Uint64: 8,
abi.Uintptr: unsafe.Sizeof(uintptr(0)),
abi.Float32: 4,
abi.Float64: 8,
abi.Complex64: 8,
abi.Complex128: 16,
abi.String: unsafe.Sizeof(String{}),
}
)
func basicType(kind abi.Kind) *Type {
return &Type{
Size_: sizeBasicTypes[kind],
Hash: uint32(kind),
Kind_: uint8(kind),
}
}
// -----------------------------------------------------------------------------

36
ssa/cl_test.go Normal file
View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ssa_test
import (
"testing"
"github.com/goplus/llgo/cl/cltest"
)
func TestFromTestrt(t *testing.T) {
cltest.FromDir(t, "", "../cl/_testrt", true)
}
func TestFromTestdata(t *testing.T) {
cltest.FromDir(t, "", "../cl/_testdata", false)
}
func TestRuntime(t *testing.T) {
cltest.Pkg(t, "github.com/goplus/llgo/internal/runtime", "../internal/runtime/llgo_autogen.ll")
cltest.Pkg(t, "github.com/goplus/llgo/internal/abi", "../internal/abi/llgo_autogen.ll")
}

View File

@@ -106,6 +106,7 @@ func (g Global) Init(v Expr) {
// respectively, and is nil in the generic method. // respectively, and is nil in the generic method.
type aFunction struct { type aFunction struct {
Expr Expr
pkg Package
prog Program prog Program
blks []BasicBlock blks []BasicBlock
@@ -116,9 +117,9 @@ type aFunction struct {
// 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, prog Program) Function { func newFunction(fn llvm.Value, t Type, pkg Package, prog Program) Function {
params, hasVArg := newParams(t, prog) params, hasVArg := newParams(t, prog)
return &aFunction{Expr{fn, t}, prog, nil, params, hasVArg} return &aFunction{Expr{fn, t}, pkg, prog, nil, params, hasVArg}
} }
func newParams(fn Type, prog Program) (params []Type, hasVArg bool) { func newParams(fn Type, prog Program) (params []Type, hasVArg bool) {
@@ -145,7 +146,8 @@ func (p Function) Param(i int) Expr {
func (p Function) NewBuilder() Builder { func (p Function) NewBuilder() Builder {
prog := p.prog prog := p.prog
b := prog.ctx.NewBuilder() b := prog.ctx.NewBuilder()
b.Finalize() // TODO(xsw): Finalize may cause panic, so comment it.
// b.Finalize()
return &aBuilder{b, p, prog} return &aBuilder{b, p, prog}
} }

View File

@@ -34,22 +34,51 @@ type Expr struct {
Type Type
} }
// ----------------------------------------------------------------------------- var Nil Expr // Zero value is a nil Expr
func llvmValues(vals []Expr) []llvm.Value { // IsNil checks if the expression is nil or not.
ret := make([]llvm.Value, len(vals)) func (v Expr) IsNil() bool {
for i, v := range vals { return v.Type == nil
ret[i] = v.impl
} }
return ret
/*
// TypeOf returns the type of the expression.
func (v Expr) TypeOf() types.Type {
return v.t
}
*/
// Do evaluates the delay expression and returns the result.
func (v Expr) Do() Expr {
if vt := v.Type; vt.kind == vkDelayExpr {
return vt.t.(delayExprTy)()
}
return v
}
// DelayExpr returns a delay expression.
func DelayExpr(f func() Expr) Expr {
return Expr{Type: &aType{t: delayExprTy(f), kind: vkDelayExpr}}
}
type delayExprTy func() Expr
func (p delayExprTy) Underlying() types.Type {
panic("don't call")
}
func (p delayExprTy) String() string {
return "delayExpr"
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// 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}
} }
// BoolVal returns a boolean constant expression.
func (p Program) BoolVal(v bool) Expr { func (p Program) BoolVal(v bool) Expr {
t := p.Bool() t := p.Bool()
var bv uint64 var bv uint64
@@ -60,15 +89,24 @@ func (p Program) BoolVal(v bool) Expr {
return Expr{ret, t} return Expr{ret, t}
} }
// IntVal returns an integer constant expression.
func (p Program) IntVal(v uint64, t Type) Expr { func (p Program) IntVal(v uint64, t Type) Expr {
ret := llvm.ConstInt(t.ll, v, false) ret := llvm.ConstInt(t.ll, v, false)
return Expr{ret, t} return Expr{ret, t}
} }
func (p Program) FloatVal(v float64, t Type) Expr {
ret := llvm.ConstFloat(t.ll, v)
return Expr{ret, t}
}
// Val returns a constant expression.
func (p Program) Val(v interface{}) Expr { func (p Program) Val(v interface{}) Expr {
switch v := v.(type) { switch v := v.(type) {
case int: case int:
return p.IntVal(uint64(v), p.Int()) return p.IntVal(uint64(v), p.Int())
case uintptr:
return p.IntVal(uint64(v), p.Uintptr())
case bool: case bool:
return p.BoolVal(v) return p.BoolVal(v)
case float64: case float64:
@@ -79,20 +117,49 @@ func (p Program) Val(v interface{}) Expr {
panic("todo") panic("todo")
} }
// Const returns a constant expression.
func (b Builder) Const(v constant.Value, typ Type) Expr { func (b Builder) Const(v constant.Value, typ Type) Expr {
prog := b.Prog
if v == nil {
return prog.Null(typ)
}
switch t := typ.t.(type) { switch t := typ.t.(type) {
case *types.Basic: case *types.Basic:
kind := t.Kind() kind := t.Kind()
switch { switch {
case kind == types.Bool: case kind == types.Bool:
return b.prog.BoolVal(constant.BoolVal(v)) return prog.BoolVal(constant.BoolVal(v))
case kind >= types.Int && kind <= types.Uintptr: case kind >= types.Int && kind <= types.Int64:
if v, exact := constant.Int64Val(v); exact {
return prog.IntVal(uint64(v), typ)
}
case kind >= types.Uint && kind <= types.Uintptr:
if v, exact := constant.Uint64Val(v); exact { if v, exact := constant.Uint64Val(v); exact {
return b.prog.IntVal(v, typ) return prog.IntVal(v, typ)
}
case kind == types.Float32 || kind == types.Float64:
if v, exact := constant.Float64Val(v); exact {
return prog.FloatVal(v, typ)
}
case kind == types.String:
return b.Str(constant.StringVal(v))
} }
} }
panic(fmt.Sprintf("unsupported Const: %v, %v", v, typ.t))
} }
panic("todo")
// CStr returns a c-style string constant expression.
func (b Builder) CStr(v string) Expr {
return Expr{llvm.CreateGlobalStringPtr(b.impl, v), b.Prog.CStr()}
}
// Str returns a Go string constant expression.
func (b Builder) Str(v string) (ret Expr) {
prog := b.Prog
cstr := b.CStr(v)
ret = b.InlineCall(b.fn.pkg.rtFunc("NewString"), cstr, prog.Val(len(v)))
ret.Type = prog.String()
return
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -219,13 +286,13 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
} }
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type} return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
case isPredOp(op): // op: == != < <= < >= case isPredOp(op): // op: == != < <= < >=
tret := b.prog.Bool() tret := b.Prog.Bool()
kind := x.kind kind := x.kind
switch kind { switch kind {
case vkSigned: case vkSigned:
pred := intPredOpToLLVM[op-predOpBase] pred := intPredOpToLLVM[op-predOpBase]
return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret} return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret}
case vkUnsigned: case vkUnsigned, vkPtr:
pred := uintPredOpToLLVM[op-predOpBase] pred := uintPredOpToLLVM[op-predOpBase]
return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret} return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret}
case vkFloat: case vkFloat:
@@ -255,24 +322,91 @@ func (b Builder) UnOp(op token.Token, x Expr) Expr {
panic("todo") panic("todo")
} }
// -----------------------------------------------------------------------------
func llvmValues(vals []Expr) []llvm.Value {
ret := make([]llvm.Value, len(vals))
for i, v := range vals {
ret[i] = v.impl
}
return ret
}
func llvmBlocks(bblks []BasicBlock) []llvm.BasicBlock {
ret := make([]llvm.BasicBlock, len(bblks))
for i, v := range bblks {
ret[i] = v.impl
}
return ret
}
// Phi represents a phi node.
type Phi struct {
Expr
}
// AddIncoming adds incoming values to a phi node.
func (p Phi) AddIncoming(vals []Expr, bblks []BasicBlock) {
v := llvmValues(vals)
b := llvmBlocks(bblks)
p.impl.AddIncoming(v, b)
}
// Phi returns a phi node.
func (b Builder) Phi(t Type) Phi {
return Phi{Expr{llvm.CreatePHI(b.impl, t.ll), t}}
}
// -----------------------------------------------------------------------------
// 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 {
log.Printf("Load %v\n", ptr.impl.Name()) log.Printf("Load %v\n", ptr.impl)
} }
telem := b.prog.Elem(ptr.Type) telem := b.Prog.Elem(ptr.Type)
return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem} return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem}
} }
// 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 {
if debugInstr { if debugInstr {
log.Printf("Store %v, %v\n", ptr.impl.Name(), val.impl) log.Printf("Store %v, %v\n", ptr.impl, val.impl)
} }
b.impl.CreateStore(val.impl, ptr.impl) b.impl.CreateStore(val.impl, ptr.impl)
return b return b
} }
// The FieldAddr instruction yields the address of Field of *struct X.
//
// The field is identified by its index within the field list of the
// struct type of X.
//
// Dynamically, this instruction panics if X evaluates to a nil
// pointer.
//
// Type() returns a (possibly named) *types.Pointer.
//
// Pos() returns the position of the ast.SelectorExpr.Sel for the
// field, if explicit in the source. For implicit selections, returns
// the position of the inducing explicit selection. If produced for a
// struct literal S{f: e}, it returns the position of the colon; for
// S{e} it returns the start of expression e.
//
// Example printed form:
//
// t1 = &t0.name [#1]
func (b Builder) FieldAddr(x Expr, idx int) Expr {
if debugInstr {
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
}
prog := b.Prog
tstruc := prog.Elem(x.Type)
telem := prog.Field(tstruc, idx)
pt := prog.Pointer(telem)
return Expr{llvm.CreateStructGEP(b.impl, tstruc.ll, x.impl, idx), pt}
}
// The IndexAddr instruction yields the address of the element at // 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.
// //
@@ -289,13 +423,86 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
if debugInstr { if debugInstr {
log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl) log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl)
} }
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) {
case *types.Slice:
pkg := b.fn.pkg
ptr := b.InlineCall(pkg.rtFunc("SliceData"), x)
indices := []llvm.Value{idx.impl}
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
}
// case *types.Pointer:
indices := []llvm.Value{idx.impl} indices := []llvm.Value{idx.impl}
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 Slice instruction yields a slice of an existing string, slice
// or *array X between optional integer bounds Low and High.
//
// Dynamically, this instruction panics if X evaluates to a nil *array
// pointer.
//
// Type() returns string if the type of X was string, otherwise a
// *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:
//
// t1 = slice t0[1:]
func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
if debugInstr {
log.Printf("Slice %v, %v, %v\n", x.impl, low.impl, high.impl)
}
prog := b.Prog
pkg := b.fn.pkg
switch t := x.t.Underlying().(type) {
case *types.Pointer:
telem := t.Elem()
switch te := telem.Underlying().(type) {
case *types.Array:
ret.Type = prog.Type(types.NewSlice(te.Elem()))
if low.IsNil() && high.IsNil() && max.IsNil() {
n := prog.Val(int(te.Len()))
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice"), x, n, n).impl
return ret
}
}
}
panic("todo")
}
// -----------------------------------------------------------------------------
// The MakeMap instruction creates a new hash-table-based map object
// and yields a value of kind 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:
//
// t1 = make map[string]int t0
// t1 = make StringIntMap t0
func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) {
if debugInstr {
log.Printf("MakeMap %v, %v\n", t, nReserve.impl)
}
pkg := b.fn.pkg
ret.Type = t
ret.impl = b.InlineCall(pkg.rtFunc("MakeSmallMap")).impl
// TODO(xsw): nReserve
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,
// zero-initializes it, and yields its address. // zero-initializes it, and yields its address.
// //
@@ -315,23 +522,285 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
// //
// t0 = local int // t0 = local int
// t1 = new int // t1 = new int
func (b Builder) Alloc(t Type, heap bool) (ret Expr) { func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("Alloc %v, %v\n", t.ll, heap) log.Printf("Alloc %v, %v\n", t, heap)
} }
telem := b.prog.Elem(t) prog := b.Prog
telem := t.Elem()
if heap { if heap {
ret.impl = llvm.CreateAlloca(b.impl, telem.ll) pkg := b.fn.pkg
size := prog.sizs.Sizeof(telem)
ret = b.Call(pkg.rtFunc("Alloc"), prog.Val(uintptr(size)))
} else { } else {
panic("todo") ret.impl = llvm.CreateAlloca(b.impl, prog.Type(telem).ll)
} }
// TODO: zero-initialize // TODO(xsw): zero-initialize
ret.Type = t ret.Type = prog.Type(t)
return return
} }
// Alloca allocates space for n bytes.
func (b Builder) Alloca(n Expr) (ret Expr) {
if debugInstr {
log.Printf("Alloca %v\n", n.impl)
}
prog := b.Prog
telem := prog.tyInt8()
ret.impl = llvm.CreateArrayAlloca(b.impl, telem, n.impl)
ret.Type = &aType{prog.tyVoidPtr(), types.Typ[types.UnsafePointer], vkPtr}
return
}
/*
// ArrayAlloca reserves space for an array of n elements of type telem.
func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) {
if debugInstr {
log.Printf("ArrayAlloca %v, %v\n", telem.t, n.impl)
}
ret.impl = llvm.CreateArrayAlloca(b.impl, telem.ll, n.impl)
ret.Type = b.Prog.Pointer(telem)
return
}
*/
// AllocaCStr allocates space for copy it from a Go string.
func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
if debugInstr {
log.Printf("AllocaCStr %v\n", gostr.impl)
}
pkg := b.fn.pkg
n := b.InlineCall(pkg.rtFunc("StringLen"), gostr)
n1 := b.BinOp(token.ADD, n, b.Prog.Val(1))
cstr := b.Alloca(n1)
return b.InlineCall(pkg.rtFunc("CStrCopy"), cstr, gostr)
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// The ChangeType instruction applies to X a value-preserving type
// change to Type().
//
// Type changes are permitted:
// - between a named type and its underlying type.
// - between two named types of the same underlying type.
// - between (possibly named) pointers to identical base types.
// - from a bidirectional channel to a read- or write-channel,
// optionally adding/removing a name.
// - between a type (t) and an instance of the type (tσ), i.e.
// Type() == σ(X.Type()) (or X.Type()== σ(Type())) where
// σ is the type substitution of Parent().TypeParams by
// Parent().TypeArgs.
//
// This operation cannot fail dynamically.
//
// Type changes may to be to or from a type parameter (or both). All
// types in the type set of X.Type() have a value-preserving type
// change to all types in the type set of Type().
//
// Example printed form:
//
// t1 = changetype *int <- IntPtr (t0)
func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
if debugInstr {
log.Printf("ChangeType %v, %v\n", t.t, x.impl)
}
typ := t.t
switch typ.(type) {
default:
ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast")
ret.Type = b.Prog.Type(typ)
return
}
}
// The Convert instruction yields the conversion of value X to type
// Type(). One or both of those types is basic (but possibly named).
//
// A conversion may change the value and representation of its operand.
// Conversions are permitted:
// - between real numeric types.
// - between complex numeric types.
// - between string and []byte or []rune.
// - between pointers and unsafe.Pointer.
// - between unsafe.Pointer and uintptr.
// - from (Unicode) integer to (UTF-8) string.
//
// A conversion may imply a type name change also.
//
// Conversions may to be to or from a type parameter. All types in
// the type set of X.Type() can be converted to all types in the type
// set of Type().
//
// This operation cannot fail dynamically.
//
// Conversions of untyped string/number/bool constants to a specific
// representation are eliminated during SSA construction.
//
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
// from an explicit conversion in the source.
//
// Example printed form:
//
// t1 = convert []byte <- string (t0)
func (b Builder) Convert(t Type, x Expr) (ret Expr) {
typ := t.t
ret.Type = b.Prog.Type(typ)
switch und := typ.Underlying().(type) {
case *types.Basic:
kind := und.Kind()
switch {
case kind >= types.Int && kind <= types.Uintptr:
ret.impl = castInt(b.impl, x.impl, t.ll)
return
case kind == types.UnsafePointer:
ret.impl = castPtr(b.impl, x.impl, t.ll)
return
}
case *types.Pointer:
ret.impl = castPtr(b.impl, x.impl, t.ll)
return
}
panic("todo")
}
func castInt(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
xt := x.Type()
if xt.TypeKind() == llvm.PointerTypeKind {
return llvm.CreatePtrToInt(b, x, t)
}
if xt.IntTypeWidth() <= t.IntTypeWidth() {
return llvm.CreateIntCast(b, x, t)
}
return llvm.CreateTrunc(b, x, t)
}
func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
if x.Type().TypeKind() == llvm.PointerTypeKind {
return llvm.CreatePointerCast(b, x, t)
}
return llvm.CreateIntToPtr(b, x, t)
}
// MakeInterface constructs an instance of an interface type from a
// value of a concrete type.
//
// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set
// of X, and Program.MethodValue(m) to find the implementation of a method.
//
// To construct the zero value of an interface type T, use:
//
// NewConst(constant.MakeNil(), T, pos)
//
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
// from an explicit conversion in the source.
//
// Example printed form:
//
// t1 = make interface{} <- int (42:int)
// t2 = make Stringer <- t0
func (b Builder) MakeInterface(inter types.Type, x Expr, mayDelay bool) (ret Expr) {
if debugInstr {
log.Printf("MakeInterface %v, %v\n", inter, x.impl)
}
tiund := inter.Underlying().(*types.Interface)
isAny := tiund.Empty()
fnDo := func() Expr {
prog := b.Prog
pkg := b.fn.pkg
tinter := prog.Type(inter)
switch tx := x.t.Underlying().(type) {
case *types.Basic:
kind := tx.Kind()
switch {
case kind >= types.Int && kind <= types.Uintptr:
t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind)))
tptr := prog.Uintptr()
vptr := Expr{llvm.CreateIntCast(b.impl, x.impl, tptr.ll), tptr}
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter}
case kind == types.String:
return Expr{b.InlineCall(pkg.rtFunc("MakeAnyString"), x).impl, tinter}
}
}
panic("todo")
}
if mayDelay && isAny {
return DelayExpr(fnDo)
}
return fnDo()
}
// The TypeAssert instruction tests whether interface value X has type
// AssertedType.
//
// If !CommaOk, on success it returns v, the result of the conversion
// (defined below); on failure it panics.
//
// If CommaOk: on success it returns a pair (v, true) where v is the
// result of the conversion; on failure it returns (z, false) where z
// is AssertedType's zero value. The components of the pair must be
// accessed using the Extract instruction.
//
// If Underlying: tests whether interface value X has the underlying
// type AssertedType.
//
// If AssertedType is a concrete type, TypeAssert checks whether the
// dynamic type in interface X is equal to it, and if so, the result
// of the conversion is a copy of the value in the interface.
//
// If AssertedType is an interface, TypeAssert checks whether the
// dynamic type of the interface is assignable to it, and if so, the
// result of the conversion is a copy of the interface value X.
// If AssertedType is a superinterface of X.Type(), the operation will
// fail iff the operand is nil. (Contrast with ChangeInterface, which
// performs no nil-check.)
//
// Type() reflects the actual type of the result, possibly a
// 2-types.Tuple; AssertedType is the asserted type.
//
// Depending on the TypeAssert's purpose, Pos may return:
// - the ast.CallExpr.Lparen of an explicit T(e) conversion;
// - the ast.TypeAssertExpr.Lparen of an explicit e.(T) operation;
// - the ast.CaseClause.Case of a case of a type-switch statement;
// - the Ident(m).NamePos of an interface method value i.m
// (for which TypeAssert may be used to effect the nil check).
//
// Example printed form:
//
// t1 = typeassert t0.(int)
// t3 = typeassert,ok t2.(T)
func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
if debugInstr {
log.Printf("TypeAssert %v, %v, %v\n", x.impl, assertedTyp.t, commaOk)
}
switch assertedTyp.kind {
case vkSigned, vkUnsigned, vkFloat:
pkg := b.fn.pkg
fnName := "I2Int"
if commaOk {
fnName = "CheckI2Int"
}
fn := pkg.rtFunc(fnName)
var kind types.BasicKind
switch t := assertedTyp.t.(type) {
case *types.Basic:
kind = t.Kind()
default:
panic("todo")
}
typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind)))
return b.InlineCall(fn, x, typ)
}
panic("todo")
}
// -----------------------------------------------------------------------------
// TODO(xsw): make inline call
func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
return b.Call(fn, args...)
}
// The Call instruction represents a function or method call. // The Call instruction represents a function or method call.
// //
// The Call instruction yields the function result if there is exactly // The Call instruction yields the function result if there is exactly
@@ -346,7 +815,7 @@ func (b Builder) Alloc(t Type, heap bool) (ret Expr) {
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
if debugInstr { if debugInstr {
var b bytes.Buffer var b bytes.Buffer
fmt.Fprint(&b, "Call @", fn.impl.Name()) fmt.Fprint(&b, "Call ", fn.impl.Name())
for _, arg := range args { for _, arg := range args {
fmt.Fprint(&b, ", ", arg.impl) fmt.Fprint(&b, ", ", arg.impl)
} }
@@ -354,7 +823,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
} }
switch t := fn.t.(type) { switch t := fn.t.(type) {
case *types.Signature: case *types.Signature:
ret.Type = b.prog.retType(t) ret.Type = b.Prog.retType(t)
default: default:
panic("todo") panic("todo")
} }
@@ -362,4 +831,24 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
return return
} }
// A Builtin represents a specific use of a built-in function, e.g. len.
//
// Builtins are immutable values. Builtins do not have addresses.
//
// `fn` indicates the function: one of the built-in functions from the
// Go spec (excluding "make" and "new").
func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
switch fn {
case "len":
if len(args) == 1 {
arg := args[0]
switch arg.t.Underlying().(type) {
case *types.Slice:
return b.InlineCall(b.fn.pkg.rtFunc("SliceLen"), arg)
}
}
}
panic("todo")
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -19,11 +19,16 @@ package ssa
import ( import (
"go/constant" "go/constant"
"go/types" "go/types"
"runtime"
"github.com/goplus/llvm" "github.com/goplus/llvm"
"golang.org/x/tools/go/types/typeutil" "golang.org/x/tools/go/types/typeutil"
) )
const (
PkgRuntime = "github.com/goplus/llgo/internal/runtime"
)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type dbgFlags = int type dbgFlags = int
@@ -93,6 +98,10 @@ func Initialize(flags InitFlags) {
type aProgram struct { type aProgram struct {
ctx llvm.Context ctx llvm.Context
typs typeutil.Map typs typeutil.Map
sizs types.Sizes
rt *types.Package
rtget func() *types.Package
target *Target target *Target
td llvm.TargetData td llvm.TargetData
@@ -107,10 +116,21 @@ type aProgram struct {
voidType llvm.Type voidType llvm.Type
voidPtrTy llvm.Type voidPtrTy llvm.Type
rtStringTy llvm.Type
rtIfaceTy llvm.Type
rtSliceTy llvm.Type
rtMapTy llvm.Type
anyTy Type
voidTy Type voidTy Type
boolTy Type boolTy Type
cstrTy Type
stringTy Type
uintptrTy Type
intTy Type intTy Type
f64Ty Type f64Ty Type
needRuntime bool
} }
// A Program presents a program. // A Program presents a program.
@@ -121,18 +141,86 @@ 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()
ctx.Finalize() 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, target: target, td: td} return &aProgram{ctx: ctx, sizs: sizes, target: target, td: td}
}
// SetRuntime sets the runtime.
// Its type can be *types.Package or func() *types.Package.
func (p Program) SetRuntime(runtime any) {
switch v := runtime.(type) {
case *types.Package:
p.rt = v
case func() *types.Package:
p.rtget = v
}
}
// NeedRuntime returns if the current package needs runtime.
func (p Program) NeedRuntime() bool {
return p.needRuntime
}
func (p Program) runtime() *types.Package {
if p.rt == nil {
p.rt = p.rtget()
}
p.needRuntime = true
return p.rt
}
func (p Program) rtNamed(name string) *types.Named {
return p.runtime().Scope().Lookup(name).Type().(*types.Named)
}
func (p Program) rtType(name string) Type {
return p.Type(p.rtNamed(name))
}
func (p Program) rtIface() llvm.Type {
if p.rtIfaceTy.IsNil() {
p.rtIfaceTy = p.rtType("Interface").ll
}
return p.rtIfaceTy
}
func (p Program) rtMap() llvm.Type {
if p.rtMapTy.IsNil() {
p.rtMapTy = p.rtType("Map").ll
}
return p.rtMapTy
}
func (p Program) rtSlice() llvm.Type {
if p.rtSliceTy.IsNil() {
p.rtSliceTy = p.rtType("Slice").ll
}
return p.rtSliceTy
}
func (p Program) rtString() llvm.Type {
if p.rtStringTy.IsNil() {
p.rtStringTy = p.rtType("String").ll
}
return p.rtStringTy
} }
// NewPackage creates a new package. // NewPackage creates a new package.
func (p Program) NewPackage(name, pkgPath string) Package { func (p Program) NewPackage(name, pkgPath string) Package {
mod := p.ctx.NewModule(pkgPath) mod := p.ctx.NewModule(pkgPath)
mod.Finalize() // TODO(xsw): Finalize may cause panic, so comment it.
// mod.Finalize()
fns := make(map[string]Function) fns := make(map[string]Function)
gbls := make(map[string]Global) gbls := make(map[string]Global)
p.needRuntime = false
return &aPackage{mod, fns, gbls, p} return &aPackage{mod, fns, gbls, p}
} }
@@ -152,6 +240,28 @@ func (p Program) Bool() Type {
return p.boolTy return p.boolTy
} }
func (p Program) CStr() Type {
if p.cstrTy == nil { // *int8
p.cstrTy = p.Type(types.NewPointer(types.Typ[types.Int8]))
}
return p.cstrTy
}
func (p Program) String() Type {
if p.stringTy == nil {
p.stringTy = p.Type(types.Typ[types.String])
}
return p.stringTy
}
// Any returns any type.
func (p Program) Any() Type {
if p.anyTy == nil {
p.anyTy = p.Type(tyAny)
}
return p.anyTy
}
// Int returns int type. // Int returns int type.
func (p Program) Int() Type { func (p Program) Int() Type {
if p.intTy == nil { if p.intTy == nil {
@@ -160,6 +270,14 @@ func (p Program) Int() Type {
return p.intTy return p.intTy
} }
// Uintptr returns uintptr type.
func (p Program) Uintptr() Type {
if p.uintptrTy == nil {
p.uintptrTy = p.Type(types.Typ[types.Uintptr])
}
return p.uintptrTy
}
// Float64 returns float64 type. // Float64 returns float64 type.
func (p Program) Float64() Type { func (p Program) Float64() Type {
if p.f64Ty == nil { if p.f64Ty == nil {
@@ -187,6 +305,7 @@ type aPackage struct {
type Package = *aPackage type Package = *aPackage
// 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{}
} }
@@ -200,11 +319,19 @@ func (p Package) NewVar(name string, typ types.Type) Global {
return ret return ret
} }
// VarOf returns a global variable by name.
func (p Package) VarOf(name string) Global {
return p.vars[name]
}
// NewFunc creates a new function. // NewFunc creates a new function.
func (p Package) NewFunc(name string, sig *types.Signature) Function { func (p Package) NewFunc(name string, sig *types.Signature) Function {
t := p.prog.llvmSignature(sig) if v, ok := p.fns[name]; ok {
return v
}
t := p.prog.llvmSignature(sig, false)
fn := llvm.AddFunction(p.mod, name, t.ll) fn := llvm.AddFunction(p.mod, name, t.ll)
ret := newFunction(fn, t, p.prog) ret := newFunction(fn, t, p, p.prog)
p.fns[name] = ret p.fns[name] = ret
return ret return ret
} }
@@ -214,9 +341,10 @@ func (p Package) FuncOf(name string) Function {
return p.fns[name] return p.fns[name]
} }
// VarOf returns a global variable by name. func (p Package) rtFunc(fnName string) Expr {
func (p Package) VarOf(name string) Global { fn := p.prog.runtime().Scope().Lookup(fnName).(*types.Func)
return p.vars[name] name := FullName(fn.Pkg(), fnName)
return p.NewFunc(name, fn.Type().(*types.Signature)).Expr
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -23,9 +23,35 @@ import (
"testing" "testing"
) )
func init() { /*
Initialize(InitAll) func TestMakeInterface(t *testing.T) {
SetDebug(DbgFlagAll) var b Builder
b.MakeInterface(types.NewInterfaceType(nil, nil), Expr{}, true).Do(true)
}
*/
func TestDelayExpr(t *testing.T) {
a := delayExprTy(nil)
_ = a.String()
defer func() {
if r := recover(); r == nil {
t.Log("TestDelayExpr: no error?")
}
}()
a.Underlying()
}
func TestAny(t *testing.T) {
prog := NewProgram(nil)
prog.SetRuntime(func() *types.Package {
ret := types.NewPackage("runtime", "runtime")
scope := ret.Scope()
name := types.NewTypeName(0, ret, "Interface", nil)
types.NewNamed(name, types.NewStruct(nil, nil), nil)
scope.Insert(name)
return ret
})
prog.Any()
} }
func assertPkg(t *testing.T, p Package, expected string) { func assertPkg(t *testing.T, p Package, expected string) {
@@ -78,6 +104,9 @@ source_filename = "foo/bar"
@a = external global {} @a = external global {}
`) `)
if prog.NeedRuntime() {
t.Fatal("NeedRuntime?")
}
} }
func TestNamedStruct(t *testing.T) { func TestNamedStruct(t *testing.T) {
@@ -93,9 +122,9 @@ func TestNamedStruct(t *testing.T) {
assertPkg(t, pkg, `; ModuleID = 'foo/bar' assertPkg(t, pkg, `; ModuleID = 'foo/bar'
source_filename = "foo/bar" source_filename = "foo/bar"
%Empty = type {} %bar.Empty = type {}
@a = external global %Empty @a = external global %bar.Empty
`) `)
} }

View File

@@ -50,7 +50,7 @@ func (p BasicBlock) Index() int {
type aBuilder struct { type aBuilder struct {
impl llvm.Builder impl llvm.Builder
fn Function fn Function
prog Program Prog Program
} }
// Builder represents a builder for creating instructions in a function. // Builder represents a builder for creating instructions in a function.
@@ -68,6 +68,21 @@ func (b Builder) SetBlock(blk BasicBlock) Builder {
return b return b
} }
// Panic emits a panic instruction.
func (b Builder) Panic(v Expr) {
if debugInstr {
log.Printf("Panic %v\n", v.impl)
}
pkg := b.fn.pkg
b.Call(pkg.rtFunc("TracePanic"), v)
b.impl.CreateUnreachable()
}
// Unreachable emits an unreachable instruction.
func (b Builder) Unreachable() {
b.impl.CreateUnreachable()
}
// Return emits a return instruction. // Return emits a return instruction.
func (b Builder) Return(results ...Expr) { func (b Builder) Return(results ...Expr) {
if debugInstr { if debugInstr {
@@ -113,4 +128,21 @@ func (b Builder) If(cond Expr, thenb, elseb BasicBlock) {
b.impl.CreateCondBr(cond.impl, thenb.impl, elseb.impl) b.impl.CreateCondBr(cond.impl, thenb.impl, elseb.impl)
} }
// The MapUpdate instruction updates the association of Map[Key] to
// Value.
//
// Pos() returns the ast.KeyValueExpr.Colon or ast.IndexExpr.Lbrack,
// if explicit in the source.
//
// Example printed form:
//
// t0[t1] = t2
func (b Builder) MapUpdate(m, k, v Expr) {
if debugInstr {
log.Printf("MapUpdate %v[%v] = %v\n", m.impl, k.impl, v.impl)
}
// TODO(xsw)
// panic("todo")
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -17,12 +17,16 @@
package ssa package ssa
import ( import (
"fmt"
"go/types" "go/types"
"log"
"github.com/goplus/llvm" "github.com/goplus/llvm"
) )
var (
tyAny = types.NewInterfaceType(nil, nil)
)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type valueKind = int type valueKind = int
@@ -35,8 +39,10 @@ const (
vkComplex vkComplex
vkString vkString
vkBool vkBool
vkPtr
vkFunc vkFunc
vkTuple vkTuple
vkDelayExpr = -1
) )
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -72,6 +78,22 @@ 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 aType struct { type aType struct {
@@ -82,6 +104,10 @@ type aType struct {
type Type = *aType type Type = *aType
func (p Program) Slice(typ Type) Type {
return p.Type(types.NewSlice(typ.t))
}
func (p Program) Pointer(typ Type) Type { func (p Program) Pointer(typ Type) Type {
return p.Type(types.NewPointer(typ.t)) return p.Type(types.NewPointer(typ.t))
} }
@@ -95,7 +121,15 @@ func (p Program) Index(typ Type) Type {
return p.Type(indexType(typ.t)) return p.Type(indexType(typ.t))
} }
func (p Program) Field(typ Type, i int) Type {
tunder := typ.t.Underlying()
return p.Type(tunder.(*types.Struct).Field(i).Type())
}
func (p Program) Type(typ types.Type) Type { func (p Program) Type(typ types.Type) Type {
if sig, ok := typ.(*types.Signature); ok { // should methodToFunc
return p.llvmSignature(sig, true)
}
if v := p.typs.At(typ); v != nil { if v := p.typs.At(typ); v != nil {
return v.(Type) return v.(Type)
} }
@@ -104,11 +138,12 @@ func (p Program) Type(typ types.Type) Type {
return ret return ret
} }
func (p Program) llvmSignature(sig *types.Signature) Type { func (p Program) llvmSignature(sig *types.Signature, isPtr bool) Type {
sig = methodToFunc(sig)
if v := p.typs.At(sig); v != nil { if v := p.typs.At(sig); v != nil {
return v.(Type) return v.(Type)
} }
ret := p.toLLVMFunc(sig) ret := p.toLLVMFunc(sig, isPtr)
p.typs.Set(sig, ret) p.typs.Set(sig, ret)
return ret return ret
} }
@@ -209,27 +244,29 @@ func (p Program) toLLVMType(typ types.Type) Type {
case types.Complex64: case types.Complex64:
case types.Complex128: case types.Complex128:
case types.String: case types.String:
return &aType{p.rtString(), typ, vkString}
case types.UnsafePointer: case types.UnsafePointer:
return &aType{p.tyVoidPtr(), typ, vkInvalid} return &aType{p.tyVoidPtr(), typ, vkPtr}
} }
case *types.Pointer: case *types.Pointer:
elem := p.Type(t.Elem()) elem := p.Type(t.Elem())
return &aType{llvm.PointerType(elem.ll, 0), typ, vkInvalid} return &aType{llvm.PointerType(elem.ll, 0), typ, vkPtr}
case *types.Interface:
return &aType{p.rtIface(), typ, vkInvalid}
case *types.Slice: case *types.Slice:
return &aType{p.rtSlice(), typ, vkInvalid}
case *types.Map: case *types.Map:
return &aType{p.rtMap(), typ, vkInvalid}
case *types.Struct: case *types.Struct:
return p.toLLVMStruct(t) return p.toLLVMStruct(t)
case *types.Named: case *types.Named:
return p.toLLVMNamed(t) return p.toLLVMNamed(t)
case *types.Signature:
return p.toLLVMFunc(t)
case *types.Array: case *types.Array:
elem := p.Type(t.Elem()) elem := p.Type(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:
} }
log.Println("toLLVMType: todo -", typ) panic(fmt.Sprintf("toLLVMType: todo - %T\n", typ))
panic("todo")
} }
func (p Program) toLLVMNamedStruct(name string, typ *types.Struct) llvm.Type { func (p Program) toLLVMNamedStruct(name string, typ *types.Struct) llvm.Type {
@@ -269,7 +306,7 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) {
return return
} }
func (p Program) toLLVMFunc(sig *types.Signature) Type { func (p Program) toLLVMFunc(sig *types.Signature, isPtr bool) Type {
tParams := sig.Params() tParams := sig.Params()
n := tParams.Len() n := tParams.Len()
hasVArg := HasVArg(tParams, n) hasVArg := HasVArg(tParams, n)
@@ -288,6 +325,9 @@ func (p Program) toLLVMFunc(sig *types.Signature) Type {
ret = p.toLLVMTuple(out) ret = p.toLLVMTuple(out)
} }
ft := llvm.FunctionType(ret, params, hasVArg) ft := llvm.FunctionType(ret, params, hasVArg)
if isPtr {
ft = llvm.PointerType(ft, 0)
}
return &aType{ft, sig, vkFunc} return &aType{ft, sig, vkFunc}
} }
@@ -304,12 +344,29 @@ func (p Program) retType(sig *types.Signature) Type {
} }
func (p Program) toLLVMNamed(typ *types.Named) Type { func (p Program) toLLVMNamed(typ *types.Named) Type {
name := typ.Obj().Name() switch t := typ.Underlying().(type) {
switch typ := typ.Underlying().(type) {
case *types.Struct: case *types.Struct:
return &aType{p.toLLVMNamedStruct(name, typ), typ, vkInvalid} name := NameOf(typ)
return &aType{p.toLLVMNamedStruct(name, t), typ, vkInvalid}
default:
return p.Type(t)
} }
panic("todo") }
func NameOf(typ *types.Named) string {
obj := typ.Obj()
return FullName(obj.Pkg(), obj.Name())
}
func FullName(pkg *types.Package, name string) string {
return PathOf(pkg) + "." + name
}
func PathOf(pkg *types.Package) string {
if pkg.Name() == "main" {
return "main"
}
return pkg.Path()
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -17,6 +17,7 @@
package clang package clang
import ( import (
"io"
"os" "os"
"os/exec" "os/exec"
) )
@@ -26,6 +27,9 @@ import (
// Cmd represents a nm command. // Cmd represents a nm command.
type Cmd struct { type Cmd struct {
app string app string
Stdout io.Writer
Stderr io.Writer
} }
// New creates a new nm command. // New creates a new nm command.
@@ -33,13 +37,13 @@ func New(app string) *Cmd {
if app == "" { if app == "" {
app = "clang" app = "clang"
} }
return &Cmd{app} return &Cmd{app, os.Stdout, os.Stderr}
} }
func (p *Cmd) Exec(args ...string) error { func (p *Cmd) Exec(args ...string) error {
cmd := exec.Command(p.app, args...) cmd := exec.Command(p.app, args...)
cmd.Stdout = os.Stdout cmd.Stdout = p.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = p.Stderr
return cmd.Run() return cmd.Run()
} }