Compare commits

..

121 Commits

Author SHA1 Message Date
xushiwei
92f56a2f90 Merge pull request #106 from xushiwei/mod
llvm v0.7.5
2024-05-05 12:15:08 +08:00
xushiwei
926e2d4a2e llvm v0.7.5 2024-05-05 12:12:57 +08:00
xushiwei
e9153defee Merge pull request #105 from xushiwei/q
cl: compile ssa.Function bugfix
2024-05-04 18:00:56 +08:00
xushiwei
889fc8b6a9 TestErrCompileValue 2024-05-04 17:59:28 +08:00
xushiwei
9b9da3133d cl: compile ssa.Function bugfix 2024-05-04 17:46:33 +08:00
xushiwei
1c8edb0387 Merge pull request #104 from xushiwei/q
llgo/ssa: checkExpr to auto convert funcPtr into closure
2024-05-04 17:28:35 +08:00
xushiwei
c0ef1598c9 llgo/ssa: checkExpr to auto convert funcPtr into closure 2024-05-04 17:25:13 +08:00
xushiwei
bc1acee6f5 Merge pull request #103 from xushiwei/q
closure
2024-05-04 07:57:00 +08:00
xushiwei
9f25d73826 intgen: todo 2024-05-04 07:53:53 +08:00
xushiwei
f07a62d136 toLLVMFunc: closure 2024-05-04 07:47:18 +08:00
xushiwei
06d6b447e4 ClosureF, ClosureData 2024-05-04 07:21:40 +08:00
xushiwei
1cff02e4cc closure 2024-05-04 07:21:07 +08:00
xushiwei
809a400f57 llgo/ssa: Call to support closure 2024-05-03 23:49:52 +08:00
xushiwei
f1bb42f554 llgo/ssa: SizeOf, MakeSlice, VoidPtr 2024-05-03 23:10:02 +08:00
xushiwei
4fd8f84536 Merge pull request #102 from xushiwei/q
cl: qsort example
2024-05-03 20:04:48 +08:00
xushiwei
223c24450e NewClosure 2024-05-03 20:02:33 +08:00
xushiwei
8a7ddf4dc2 cl: qsort example 2024-05-03 19:59:56 +08:00
xushiwei
08217e5a5a Merge pull request #101 from xushiwei/q
TestTypes
2024-05-03 19:23:02 +08:00
xushiwei
424dbd9261 TestTypes 2024-05-03 19:20:30 +08:00
xushiwei
b615ada2c3 Merge pull request #99 from visualfc/slice3
update ssa.Slice
2024-05-03 19:09:29 +08:00
xushiwei
20a47873d0 Merge pull request #100 from xushiwei/q
llgo/ssa: vkFuncDecl/vkFuncPtr/vkClosure
2024-05-03 19:08:12 +08:00
xushiwei
d87ce1a124 cl: qsort 2024-05-03 19:05:49 +08:00
xushiwei
91d012d33d TestCvtCType 2024-05-03 19:02:17 +08:00
xushiwei
330cb22351 TestErrCompileInstrOrValue 2024-05-03 18:35:14 +08:00
xushiwei
236debab33 CFuncPtr.Hash 2024-05-03 18:02:09 +08:00
xushiwei
13a1c8ac4b github.com/goplus/llgo/internal/typeutil.Map 2024-05-03 17:39:37 +08:00
xushiwei
29fad7b397 TestCFuncPtr 2024-05-03 17:08:44 +08:00
xushiwei
8eeac8a26d cvtCType bugfix 2024-05-03 17:03:12 +08:00
xushiwei
133d41d748 llgo/ssa: CType, CFuncDecl 2024-05-03 16:51:01 +08:00
visualfc
d444123062 update ssa.Slice 2024-05-03 16:30:33 +08:00
xushiwei
4a5c8d3fbb cl: call llgo/ssa.CType/CFuncDecl 2024-05-03 16:00:31 +08:00
xushiwei
afd3d40348 llgo/ssa: vkFuncDecl/vkFuncPtr/vkClosure 2024-05-03 15:40:24 +08:00
xushiwei
85da86a4f1 Merge pull request #98 from xushiwei/q
cl: link go name
2024-05-03 00:00:42 +08:00
xushiwei
72d4f0f7f8 Merge pull request #97 from visualfc/cap
ssa: builtin len&cap
2024-05-02 23:58:12 +08:00
xushiwei
192b479f18 cl: link go name 2024-05-02 23:14:12 +08:00
visualfc
3e6dfa3c05 ssa: builtin len&cap 2024-05-02 16:26:45 +08:00
xushiwei
5bd28a1e9e Merge pull request #96 from xushiwei/q
runtime: stringTracef
2024-05-02 13:17:02 +08:00
xushiwei
a23a2601e4 runtime: stringTracef 2024-05-02 13:10:41 +08:00
xushiwei
3220b629c7 Merge pull request #95 from xushiwei/q
runtime: StringCat; llgo/ssa: aggregateValue
2024-05-02 12:41:37 +08:00
xushiwei
9cf122c31a runtime: StringCat; llgo/ssa: aggregateValue 2024-05-02 12:39:25 +08:00
xushiwei
75d513a78a Merge pull request #94 from xushiwei/q
cl: compilePhis; llgo/ssa: phi.AddIncoming
2024-05-02 11:51:49 +08:00
xushiwei
3cbe4aac87 TestUserdefExpr 2024-05-02 11:38:05 +08:00
xushiwei
3e47a977e4 update llvm 2024-05-02 11:31:33 +08:00
xushiwei
40855c2d2a cl: compilePhis; llgo/ssa: phi.AddIncoming 2024-05-02 11:27:02 +08:00
xushiwei
b2319eda66 Merge pull request #92 from visualfc/index
implement ssa.index
2024-05-02 10:35:32 +08:00
visualfc
5c5b8e62e5 implement ssa.index 2024-05-02 09:26:56 +08:00
xushiwei
fbb1f89ab3 phisExpr as an AggregateVal 2024-05-02 07:56:52 +08:00
xushiwei
25b104cf13 llgo/ssa: PhisExpr 2024-05-02 07:37:31 +08:00
xushiwei
be1599b418 Merge pull request #93 from xushiwei/q
runtime: StringCat; builtin: llgo.advance
2024-05-02 00:08:04 +08:00
xushiwei
d462e548b1 TestErrAdvance 2024-05-02 00:04:37 +08:00
xushiwei
df1e4708f5 builtin: llgo.advance 2024-05-01 23:57:19 +08:00
xushiwei
cf02f4a34f runtime: StringCat 2024-05-01 22:00:34 +08:00
xushiwei
480cf09177 Merge pull request #91 from xushiwei/q
cl: support stdin/stdout/stderr
2024-05-01 21:26:20 +08:00
xushiwei
52a64a7770 cl: initLinknameByDoc 2024-05-01 21:18:28 +08:00
xushiwei
8d3cb246c2 cl: linkname of varName 2024-05-01 20:56:51 +08:00
xushiwei
8f15fd45f2 initLinkname: support var 2024-05-01 20:33:46 +08:00
xushiwei
afd02b3d78 cl: to support stdin/stdout/stderr 2024-05-01 20:05:28 +08:00
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
96 changed files with 6765 additions and 464 deletions

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@
*.so
*.dylib
.DS_Store
err.log
_go/

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,8 +19,6 @@ package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/goplus/llgo/internal/llgen"
)
@@ -31,24 +29,6 @@ func main() {
return
}
inFile := os.Args[1]
dir, _ := filepath.Split(inFile)
fname := "llgo_autogen.ll"
if inCompilerDir(dir) {
fname = "out.ll"
}
outFile := dir + fname
llgen.Init()
if len(os.Args) >= 3 {
llgen.Do(os.Args[2], inFile, outFile)
} else {
llgen.DoFile(inFile, outFile)
}
}
func inCompilerDir(dir string) bool {
dir, _ = filepath.Abs(dir)
return strings.Contains(filepath.ToSlash(dir), "/llgo/cl/")
llgen.SmartDoFile(os.Args[1], os.Args[2:]...)
}

View File

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

View File

@@ -18,6 +18,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
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
@@ -34,3 +35,5 @@ _llgo_1: ; preds = %_llgo_0
_llgo_2: ; preds = %_llgo_0
ret i64 %1
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -27,6 +27,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64 2, i64 100)
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/internal/runtime.init"()
declare i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64, i64)
declare void @printf(ptr, ...)

View File

@@ -42,6 +42,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
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)
@@ -49,3 +50,5 @@ _llgo_0:
}
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -26,9 +26,12 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
call void (ptr, ...) @printf(ptr @main.hello)
ret void
}
declare void @printf(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -29,9 +29,12 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
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

@@ -35,9 +35,12 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
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

@@ -20,6 +20,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = load i64, ptr @main.a, align 4
%1 = add i64 %0, 1
@@ -27,3 +28,5 @@ _llgo_0:
%2 = load i64, ptr @main.a, align 4
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,49 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, 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.String")
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))
}

View File

@@ -3,8 +3,8 @@ source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
@main.format = global ptr null
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1
define i64 @main.incVal(%"github.com/goplus/llgo/internal/runtime.iface" %0) {
_llgo_0:
@@ -21,16 +21,6 @@ _llgo_0:
_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
@@ -39,17 +29,21 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
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.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(i64 100)
%1 = call i64 @main.incVal(%"github.com/goplus/llgo/internal/runtime.iface" %0)
call void (ptr, ...) @printf(ptr @main.format, i64 %1)
%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 void @printf(ptr, ...)
declare i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface", ptr)
declare ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64)
declare 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, ...)

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

@@ -0,0 +1,51 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
var a int64 = 1<<63 - 1
var b int64 = -1 << 63
var n uint64 = 1<<64 - 1
func main() {
var s = []int{1, 2, 3, 4}
var a = [...]int{1, 2, 3, 4}
out(len(s))
out(len([]int{1, 2, 3, 4}))
out(len(a))
out(len(&a))
out(len([4]int{1, 2, 3, 4}))
out(cap(s))
out(cap(a))
out(cap(&a))
out(len(s[1:]))
out(cap(s[1:]))
out(len(s[1:2]))
out(cap(s[1:2]))
out(len(s[1:2:2]))
out(cap(s[1:2:2]))
out(len(a[1:]))
out(cap(a[1:]))
out(len(a[1:2]))
out(cap(a[1:2]))
out(len(a[1:2:2]))
out(cap(a[1:2:2]))
string_len("hello")
string_len("hello"[1:])
string_len("hello"[1:2])
string_len("hello"[5:])
}
func string_len(s string) {
out(len(s))
}
func out(n int) {
c.Printf(c.Str("%d\n"), n)
}

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

@@ -0,0 +1,174 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
@main.a = global ptr null
@main.b = global ptr null
@"main.init$guard" = global ptr null
@main.n = global ptr null
@0 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
@1 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
@2 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
@3 = private unnamed_addr constant [6 x i8] c"hello\00", align 1
@4 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
define void @main.init() {
_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.n, 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.AllocZ"(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.NewSlice3"(ptr %0, i64 8, i64 4, i64 0, i64 4, i64 4)
%6 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
%7 = getelementptr inbounds i64, ptr %6, i64 0
%8 = getelementptr inbounds i64, ptr %6, i64 1
%9 = getelementptr inbounds i64, ptr %6, i64 2
%10 = getelementptr inbounds i64, ptr %6, i64 3
store i64 1, ptr %7, align 4
store i64 2, ptr %8, align 4
store i64 3, ptr %9, align 4
store i64 4, ptr %10, align 4
%11 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
call void @main.out(i64 %11)
%12 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
%13 = getelementptr inbounds i64, ptr %12, i64 0
store i64 1, ptr %13, align 4
%14 = getelementptr inbounds i64, ptr %12, i64 1
store i64 2, ptr %14, align 4
%15 = getelementptr inbounds i64, ptr %12, i64 2
store i64 3, ptr %15, align 4
%16 = getelementptr inbounds i64, ptr %12, i64 3
store i64 4, ptr %16, align 4
%17 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %12, i64 8, i64 4, i64 0, i64 4, i64 4)
%18 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %17)
call void @main.out(i64 %18)
call void @main.out(i64 4)
call void @main.out(i64 4)
call void @main.out(i64 4)
%19 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
call void @main.out(i64 %19)
call void @main.out(i64 4)
call void @main.out(i64 4)
%20 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%21 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%22 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%23 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %22, i64 8, i64 %20, i64 1, i64 %21, i64 %20)
%24 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %23)
call void @main.out(i64 %24)
%25 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%26 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%27 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%28 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %27, i64 8, i64 %25, i64 1, i64 %26, i64 %25)
%29 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %28)
call void @main.out(i64 %29)
%30 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%31 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%32 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %31, i64 8, i64 %30, i64 1, i64 2, i64 %30)
%33 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %32)
call void @main.out(i64 %33)
%34 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%35 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%36 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %35, i64 8, i64 %34, i64 1, i64 2, i64 %34)
%37 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %36)
call void @main.out(i64 %37)
%38 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%39 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%40 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %39, i64 8, i64 %38, i64 1, i64 2, i64 2)
%41 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %40)
call void @main.out(i64 %41)
%42 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%43 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%44 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %43, i64 8, i64 %42, i64 1, i64 2, i64 2)
%45 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %44)
call void @main.out(i64 %45)
%46 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 4, i64 4)
%47 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %46)
call void @main.out(i64 %47)
%48 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 4, i64 4)
%49 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %48)
call void @main.out(i64 %49)
%50 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 2, i64 4)
%51 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %50)
call void @main.out(i64 %51)
%52 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 2, i64 4)
%53 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %52)
call void @main.out(i64 %53)
%54 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 2, i64 2)
%55 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %54)
call void @main.out(i64 %55)
%56 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %6, i64 8, i64 4, i64 1, i64 2, i64 2)
%57 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %56)
call void @main.out(i64 %57)
%58 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 5)
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %58)
%59 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 5)
%60 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %59)
%61 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %59, i64 1, i64 %60)
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %61)
%62 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 5)
%63 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %62, i64 1, i64 2)
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %63)
%64 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 5)
%65 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %64)
%66 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %64, i64 5, i64 %65)
call void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %66)
ret void
}
define void @main.out(i64 %0) {
_llgo_0:
%1 = call i32 (ptr, ...) @printf(ptr @4, i64 %0)
ret void
}
define void @main.string_len(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0:
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %0)
call void @main.out(i64 %1)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String", i64, i64)
declare i32 @printf(ptr, ...)

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

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

106
cl/_testrt/concat/out.ll Normal file
View File

@@ -0,0 +1,106 @@
; ModuleID = 'main'
source_filename = "main"
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
@1 = private unnamed_addr constant [6 x i8] c"Hello\00", align 1
@2 = private unnamed_addr constant [2 x i8] c" \00", align 1
@3 = private unnamed_addr constant [6 x i8] c"World\00", align 1
@__stderrp = external global ptr
@4 = private unnamed_addr constant [8 x i8] c"Hi, %s\0A\00", align 1
define %"github.com/goplus/llgo/internal/runtime.String" @main.concat(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 0)
%3 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %2, 0
%4 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %2, 1
br label %_llgo_1
_llgo_1: ; preds = %_llgo_2, %_llgo_0
%5 = phi ptr [ %3, %_llgo_0 ], [ %18, %_llgo_2 ]
%6 = phi i64 [ %4, %_llgo_0 ], [ %19, %_llgo_2 ]
%7 = phi i64 [ -1, %_llgo_0 ], [ %12, %_llgo_2 ]
%8 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %8, i32 0, i32 0
store ptr %5, ptr %9, align 8
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %8, i32 0, i32 1
store i64 %6, ptr %10, align 4
%11 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %8, align 8
%12 = add i64 %7, 1
%13 = icmp slt i64 %12, %1
br i1 %13, label %_llgo_2, label %_llgo_3
_llgo_2: ; preds = %_llgo_1
%14 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
%15 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %14, i64 %12
%16 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %15, align 8
%17 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String" %11, %"github.com/goplus/llgo/internal/runtime.String" %16)
%18 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %17, 0
%19 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %17, 1
br label %_llgo_1
_llgo_3: ; preds = %_llgo_1
ret %"github.com/goplus/llgo/internal/runtime.String" %11
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 48)
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i64 0
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 5)
store %"github.com/goplus/llgo/internal/runtime.String" %2, ptr %1, align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i64 1
%4 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 1)
store %"github.com/goplus/llgo/internal/runtime.String" %4, ptr %3, align 8
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i64 2
%6 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 5)
store %"github.com/goplus/llgo/internal/runtime.String" %6, ptr %5, align 8
%7 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %0, i64 16, i64 3, i64 0, i64 3, i64 3)
%8 = call %"github.com/goplus/llgo/internal/runtime.String" @main.concat(%"github.com/goplus/llgo/internal/runtime.Slice" %7)
%9 = load ptr, ptr @__stderrp, align 8
%10 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %8)
%11 = add i64 %10, 1
%12 = alloca i8, i64 %11, align 1
%13 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %12, %"github.com/goplus/llgo/internal/runtime.String" %8)
%14 = call i32 (ptr, ptr, ...) @fprintf(ptr %9, ptr @4, ptr %13)
ret void
}
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String", %"github.com/goplus/llgo/internal/runtime.String")
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String")
declare i32 @fprintf(ptr, ptr, ...)

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

19
cl/_testrt/cvar/in.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import _ "unsafe"
//go:linkname barX _bar_x
var barX struct {
Arr [16]int8
Callbacks [2]func()
}
//go:linkname barY _bar_y
var barY struct {
Arr [16]int8
}
func main() {
_ = barX
_ = barY
}

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

@@ -0,0 +1,30 @@
; ModuleID = 'main'
source_filename = "main"
@_bar_x = external global ptr
@_bar_y = external global ptr
@"main.init$guard" = global ptr null
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = load { [16 x i8], [2 x ptr] }, ptr @_bar_x, align 8
%1 = load { [16 x i8] }, ptr @_bar_y, align 1
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()

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

@@ -0,0 +1,16 @@
package main
import "unsafe"
//go:linkname cstr llgo.cstr
func cstr(string) *int8
//go:linkname stderr __stderrp
var stderr unsafe.Pointer
//go:linkname fprintf C.fprintf
func fprintf(fp unsafe.Pointer, format *int8, __llgo_va_list ...any)
func main() {
fprintf(stderr, cstr("Hello %d\n"), 100)
}

32
cl/_testrt/fprintf/out.ll Normal file
View File

@@ -0,0 +1,32 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@__stderrp = external global ptr
@0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1
declare void @fprintf(ptr, ptr, ...)
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = load ptr, ptr @__stderrp, align 8
call void (ptr, ptr, ...) @fprintf(ptr %0, ptr @0, i64 100)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()

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 }, 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.AllocZ"(i64 56)
%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.AllocZ"(i64)
declare void @"github.com/goplus/llgo/internal/abi.init"()
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i32 @printf(ptr, ...)

View File

@@ -29,12 +29,15 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
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 void @"github.com/goplus/llgo/internal/runtime.init"()
declare i32 @strlen(ptr)
declare void @printf(ptr, ...)

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

@@ -0,0 +1,25 @@
package main
import "github.com/goplus/llgo/internal/runtime/c"
type point struct {
x int
y int
}
func main() {
a := [...]point{{1, 2}, {3, 4}, {5, 6}}[2]
c.Printf(c.Str("%d %d\n"), a.x, a.y)
b := [...][2]int{[2]int{1, 2}, [2]int{3, 4}}[1]
c.Printf(c.Str("%d %d\n"), b[0], b[1])
var i int = 2
n := [...]int{1, 2, 3, 4, 5}[i]
c.Printf(c.Str("%d\n"), n)
c.Printf(c.Str("%d\n"), [...]int{1, 2, 3, 4, 5}[i])
s := "123456"
c.Printf(c.Str("%c\n"), s[i])
c.Printf(c.Str("%c\n"), "123456"[1])
}

138
cl/_testrt/index/out.ll Normal file
View File

@@ -0,0 +1,138 @@
; ModuleID = 'main'
source_filename = "main"
%main.point = type { i64, i64 }
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1
@1 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1
@2 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@3 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@4 = private unnamed_addr constant [4 x i8] c"%c\0A\00", align 1
@5 = private unnamed_addr constant [7 x i8] c"123456\00", align 1
@6 = private unnamed_addr constant [4 x i8] c"%c\0A\00", align 1
@7 = private unnamed_addr constant [7 x i8] c"123456\00", align 1
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = alloca %main.point, align 8
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 16)
%2 = alloca [3 x %main.point], align 8
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 48)
%4 = getelementptr inbounds %main.point, ptr %3, i64 0
%5 = getelementptr inbounds %main.point, ptr %4, i32 0, i32 0
%6 = getelementptr inbounds %main.point, ptr %4, i32 0, i32 1
%7 = getelementptr inbounds %main.point, ptr %3, i64 1
%8 = getelementptr inbounds %main.point, ptr %7, i32 0, i32 0
%9 = getelementptr inbounds %main.point, ptr %7, i32 0, i32 1
%10 = getelementptr inbounds %main.point, ptr %3, i64 2
%11 = getelementptr inbounds %main.point, ptr %10, i32 0, i32 0
%12 = getelementptr inbounds %main.point, ptr %10, i32 0, i32 1
store i64 1, ptr %5, align 4
store i64 2, ptr %6, align 4
store i64 3, ptr %8, align 4
store i64 4, ptr %9, align 4
store i64 5, ptr %11, align 4
store i64 6, ptr %12, align 4
%13 = load [3 x %main.point], ptr %3, align 4
%14 = getelementptr inbounds %main.point, ptr %3, i64 2
%15 = load %main.point, ptr %14, align 4
store %main.point %15, ptr %1, align 4
%16 = getelementptr inbounds %main.point, ptr %1, i32 0, i32 0
%17 = load i64, ptr %16, align 4
%18 = getelementptr inbounds %main.point, ptr %1, i32 0, i32 1
%19 = load i64, ptr %18, align 4
%20 = call i32 (ptr, ...) @printf(ptr @0, i64 %17, i64 %19)
%21 = alloca [2 x i64], align 8
%22 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %21, i64 16)
%23 = alloca [2 x [2 x i64]], align 8
%24 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %23, i64 32)
%25 = getelementptr inbounds [2 x i64], ptr %24, i64 0
%26 = getelementptr inbounds i64, ptr %25, i64 0
%27 = getelementptr inbounds i64, ptr %25, i64 1
%28 = getelementptr inbounds [2 x i64], ptr %24, i64 1
%29 = getelementptr inbounds i64, ptr %28, i64 0
%30 = getelementptr inbounds i64, ptr %28, i64 1
store i64 1, ptr %26, align 4
store i64 2, ptr %27, align 4
store i64 3, ptr %29, align 4
store i64 4, ptr %30, align 4
%31 = load [2 x [2 x i64]], ptr %24, align 4
%32 = getelementptr inbounds [2 x i64], ptr %24, i64 1
%33 = load [2 x i64], ptr %32, align 4
store [2 x i64] %33, ptr %22, align 4
%34 = getelementptr inbounds i64, ptr %22, i64 0
%35 = load i64, ptr %34, align 4
%36 = getelementptr inbounds i64, ptr %22, i64 1
%37 = load i64, ptr %36, align 4
%38 = call i32 (ptr, ...) @printf(ptr @1, i64 %35, i64 %37)
%39 = alloca [5 x i64], align 8
%40 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %39, i64 40)
%41 = getelementptr inbounds i64, ptr %40, i64 0
%42 = getelementptr inbounds i64, ptr %40, i64 1
%43 = getelementptr inbounds i64, ptr %40, i64 2
%44 = getelementptr inbounds i64, ptr %40, i64 3
%45 = getelementptr inbounds i64, ptr %40, i64 4
store i64 1, ptr %41, align 4
store i64 2, ptr %42, align 4
store i64 3, ptr %43, align 4
store i64 4, ptr %44, align 4
store i64 5, ptr %45, align 4
%46 = load [5 x i64], ptr %40, align 4
%47 = getelementptr inbounds i64, ptr %40, i64 2
%48 = load i64, ptr %47, align 4
%49 = call i32 (ptr, ...) @printf(ptr @2, i64 %48)
%50 = alloca [5 x i64], align 8
%51 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %50, i64 40)
%52 = getelementptr inbounds i64, ptr %51, i64 0
%53 = getelementptr inbounds i64, ptr %51, i64 1
%54 = getelementptr inbounds i64, ptr %51, i64 2
%55 = getelementptr inbounds i64, ptr %51, i64 3
%56 = getelementptr inbounds i64, ptr %51, i64 4
store i64 1, ptr %52, align 4
store i64 2, ptr %53, align 4
store i64 3, ptr %54, align 4
store i64 4, ptr %55, align 4
store i64 5, ptr %56, align 4
%57 = load [5 x i64], ptr %51, align 4
%58 = getelementptr inbounds i64, ptr %51, i64 2
%59 = load i64, ptr %58, align 4
%60 = call i32 (ptr, ...) @printf(ptr @3, i64 %59)
%61 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @5, i64 6)
%62 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %61)
%63 = getelementptr inbounds i8, ptr %62, i64 2
%64 = load i8, ptr %63, align 1
%65 = call i32 (ptr, ...) @printf(ptr @4, i8 %64)
%66 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @7, i64 6)
%67 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %66)
%68 = getelementptr inbounds i8, ptr %67, i64 1
%69 = load i8, ptr %68, align 1
%70 = call i32 (ptr, ...) @printf(ptr @6, i8 %69)
ret void
}
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64)
declare i32 @printf(ptr, ...)
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
declare ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String")

20
cl/_testrt/intgen/in.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func genInts(n int, gen func() c.Int) []c.Int {
a := make([]c.Int, n)
for i := range a {
a[i] = gen()
}
return a
}
func main() {
a := genInts(5, c.Rand)
for _, v := range a {
c.Printf(c.Str("%d\n"), v)
}
}

90
cl/_testrt/intgen/out.ll Normal file
View File

@@ -0,0 +1,90 @@
; 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 [4 x i8] c"%d\0A\00", align 1
define %"github.com/goplus/llgo/internal/runtime.Slice" @main.genInts(i64 %0, { ptr, ptr } %1) {
_llgo_0:
%2 = mul i64 %0, 4
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 %2)
%4 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %3, i64 %0, i64 %0)
%5 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %4)
br label %_llgo_1
_llgo_1: ; preds = %_llgo_2, %_llgo_0
%6 = phi i64 [ -1, %_llgo_0 ], [ %7, %_llgo_2 ]
%7 = add i64 %6, 1
%8 = icmp slt i64 %7, %5
br i1 %8, label %_llgo_2, label %_llgo_3
_llgo_2: ; preds = %_llgo_1
%9 = extractvalue { ptr, ptr } %1, 0
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %4)
%11 = getelementptr inbounds i32, ptr %10, i64 %7
store i32 0, ptr %11, align 4
br label %_llgo_1
_llgo_3: ; preds = %_llgo_1
ret %"github.com/goplus/llgo/internal/runtime.Slice" %4
}
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = alloca { ptr, ptr }, align 8
%1 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 0
store ptr @rand, ptr %1, align 8
%2 = getelementptr inbounds { ptr, ptr }, ptr %0, i32 0, i32 1
store ptr null, ptr %2, align 8
%3 = load { ptr, ptr }, ptr %0, align 8
%4 = call %"github.com/goplus/llgo/internal/runtime.Slice" @main.genInts(i64 5, { ptr, ptr } %3)
%5 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %4)
br label %_llgo_1
_llgo_1: ; preds = %_llgo_2, %_llgo_0
%6 = phi i64 [ -1, %_llgo_0 ], [ %7, %_llgo_2 ]
%7 = add i64 %6, 1
%8 = icmp slt i64 %7, %5
br i1 %8, label %_llgo_2, label %_llgo_3
_llgo_2: ; preds = %_llgo_1
%9 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %4)
%10 = getelementptr inbounds i32, ptr %9, i64 %7
%11 = load i32, ptr %10, align 4
%12 = call i32 (ptr, ...) @printf(ptr @0, i32 %11)
br label %_llgo_1
_llgo_3: ; preds = %_llgo_1
ret void
}
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr, i64, i64)
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice")
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare i32 @rand()
declare i32 @printf(ptr, ...)

15
cl/_testrt/linkname/in.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
_ "unsafe"
"github.com/goplus/llgo/internal/runtime/c"
)
//go:linkname print github.com/goplus/llgo/cl/internal/linktarget.F
func print(a, b, c, d *c.Char)
func main() {
print(c.Str("a"), c.Str("b"), c.Str("c"), c.Str("d"))
print(c.Str("1"), c.Str("2"), c.Str("3"), c.Str("4"))
}

View File

@@ -0,0 +1,38 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [2 x i8] c"a\00", align 1
@1 = private unnamed_addr constant [2 x i8] c"b\00", align 1
@2 = private unnamed_addr constant [2 x i8] c"c\00", align 1
@3 = private unnamed_addr constant [2 x i8] c"d\00", align 1
@4 = private unnamed_addr constant [2 x i8] c"1\00", align 1
@5 = private unnamed_addr constant [2 x i8] c"2\00", align 1
@6 = private unnamed_addr constant [2 x i8] c"3\00", align 1
@7 = private unnamed_addr constant [2 x i8] c"4\00", align 1
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
call void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr @0, ptr @1, ptr @2, ptr @3)
call void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr @4, ptr @5, ptr @6, ptr @7)
ret void
}
declare void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr, ptr, ptr, ptr)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

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

20
cl/_testrt/qsort/in.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"unsafe"
"github.com/goplus/llgo/internal/runtime/c"
)
//go:linkname qsort C.qsort
func qsort(base c.Pointer, count, elem uintptr, compar func(a, b c.Pointer) c.Int)
func main() {
a := [...]int{100, 8, 23, 2, 7}
qsort(c.Pointer(&a[0]), 5, unsafe.Sizeof(0), func(a, b c.Pointer) c.Int {
return c.Int(*(*int)(a) - *(*int)(b))
})
for _, v := range a {
c.Printf(c.Str("%d\n"), v)
}
}

71
cl/_testrt/qsort/out.ll Normal file
View File

@@ -0,0 +1,71 @@
; ModuleID = 'main'
source_filename = "main"
@"main.init$guard" = global ptr null
@0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
define void @main.init() {
_llgo_0:
%0 = load i1, ptr @"main.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
define void @main() {
_llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 40)
%1 = getelementptr inbounds i64, ptr %0, i64 0
%2 = getelementptr inbounds i64, ptr %0, i64 1
%3 = getelementptr inbounds i64, ptr %0, i64 2
%4 = getelementptr inbounds i64, ptr %0, i64 3
%5 = getelementptr inbounds i64, ptr %0, i64 4
store i64 100, ptr %1, align 4
store i64 8, ptr %2, align 4
store i64 23, ptr %3, align 4
store i64 2, ptr %4, align 4
store i64 7, ptr %5, align 4
%6 = getelementptr inbounds i64, ptr %0, i64 0
call void @qsort(ptr %6, i64 5, i64 8, ptr @"main.main$1")
%7 = load [5 x i64], ptr %0, align 4
br label %_llgo_1
_llgo_1: ; preds = %_llgo_2, %_llgo_0
%8 = phi i64 [ -1, %_llgo_0 ], [ %9, %_llgo_2 ]
%9 = add i64 %8, 1
%10 = icmp slt i64 %9, 5
br i1 %10, label %_llgo_2, label %_llgo_3
_llgo_2: ; preds = %_llgo_1
%11 = getelementptr inbounds i64, ptr %0, i64 %9
%12 = load i64, ptr %11, align 4
%13 = call i32 (ptr, ...) @printf(ptr @0, i64 %12)
br label %_llgo_1
_llgo_3: ; preds = %_llgo_1
ret void
}
declare void @qsort(ptr, i64, i64, ptr)
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
define i32 @"main.main$1"(ptr %0, ptr %1) {
_llgo_0:
%2 = load i64, ptr %0, align 4
%3 = load i64, ptr %1, align 4
%4 = sub i64 %2, %3
%5 = trunc i64 %4 to i32
ret i32 %5
}
declare i32 @printf(ptr, ...)

View File

@@ -11,7 +11,6 @@ _llgo_0:
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"main.init$guard", align 1
call void @"github.com/goplus/llgo/cl/internal/libc.init"()
store i8 72, ptr @main.format, align 1
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
@@ -30,14 +29,15 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
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, ...) @"github.com/goplus/llgo/cl/internal/libc.Printf"(ptr @main.format, i32 %0)
call void (ptr, ...) @printf(ptr @main.format, i32 %0)
ret void
}
declare void @"github.com/goplus/llgo/cl/internal/libc.init"()
declare void @printf(ptr, ...)
declare i32 @strlen(ptr)
declare void @"github.com/goplus/llgo/cl/internal/libc.Printf"(ptr, ...)
declare void @"github.com/goplus/llgo/internal/runtime.init"()

View File

@@ -9,15 +9,16 @@ source_filename = "main"
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
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 8)
store %main.Foo %0, ptr %2, align 4
%3 = getelementptr inbounds %main.Foo, ptr %2, i32 0, i32 1
%4 = load i1, ptr %3, align 1
br i1 %4, 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)
%5 = getelementptr inbounds %main.Foo, ptr %2, i32 0, i32 0
%6 = load i32, ptr %5, align 4
call void (ptr, ...) @printf(ptr @main.format, i32 %6)
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
@@ -56,15 +57,21 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
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)
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 8)
%2 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 0
%3 = getelementptr inbounds %main.Foo, ptr %1, i32 0, i32 1
store i32 100, ptr %2, align 4
store i1 true, ptr %3, align 1
%4 = load %main.Foo, ptr %1, align 4
call void @"(main.Foo).Print"(%main.Foo %4)
ret void
}
declare void @printf(ptr, ...)
declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64)
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.AllocZ"(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.NewSlice3"(ptr %0, i64 8, i64 4, i64 0, i64 4, i64 4)
%6 = call i64 @main.sum(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
%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.AllocZ"(i64)
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
declare i32 @printf(ptr, ...)
declare 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")

View File

@@ -45,8 +45,9 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
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 16)
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8)
%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
@@ -57,4 +58,6 @@ _llgo_0:
declare void @printf(ptr, ...)
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64)
declare void @"github.com/goplus/llgo/internal/runtime.init"()
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(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, ...)

View File

@@ -17,6 +17,7 @@
package cl
import (
"go/constant"
"go/types"
"testing"
@@ -24,6 +25,110 @@ import (
"golang.org/x/tools/go/ssa"
)
func TestErrCompileValue(t *testing.T) {
defer func() {
if r := recover(); r != "can't use llgo instruction as a value" {
t.Fatal("TestErrCompileValue:", r)
}
}()
pkg := types.NewPackage("foo", "foo")
ctx := &context{
goTyps: pkg,
link: map[string]string{
"foo.": "llgo.unreachable",
},
}
ctx.compileValue(nil, &ssa.Function{
Pkg: &ssa.Package{Pkg: pkg},
Signature: types.NewSignatureType(nil, nil, nil, nil, nil, false),
})
}
func TestErrCompileInstrOrValue(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("compileInstrOrValue: no error?")
}
}()
ctx := &context{
bvals: make(map[ssa.Value]llssa.Expr),
}
ctx.compileInstrOrValue(nil, &ssa.Call{}, true)
}
func TestErrAdvance(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("advance: no error?")
}
}()
var ctx context
ctx.advance(nil, nil)
}
func TestErrAlloca(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatal("alloca: no error?")
}
}()
var ctx context
ctx.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?")
@@ -48,7 +153,7 @@ func TestIgnoreName(t *testing.T) {
func TestErrImport(t *testing.T) {
var ctx context
pkg := types.NewPackage("foo", "foo")
ctx.importPkg(pkg)
ctx.importPkg(pkg, nil)
}
func TestErrInitLinkname(t *testing.T) {
@@ -58,7 +163,7 @@ func TestErrInitLinkname(t *testing.T) {
}
}()
var ctx context
ctx.initLinkname("foo", "//go:linkname Printf printf")
ctx.initLinkname("foo", "//go:linkname Printf printf", false)
}
func TestErrVarOf(t *testing.T) {

View File

@@ -113,6 +113,14 @@ func TestCompileEx(t *testing.T, src any, fname, expected string) {
}
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)

View File

@@ -61,12 +61,12 @@ const (
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 {
params := fn.Signature.Params()
n := params.Len()
if n == 0 {
if fn.Name() == "init" && ignorePkgInit(fn.Pkg.Pkg.Path()) {
if fn.Name() == "init" && p.pkgNoInit(fn.Pkg.Pkg) {
return fnIgnore
}
} else {
@@ -79,10 +79,10 @@ func funcKind(vfn ssa.Value) int {
return fnNormal
}
func ignorePkgInit(pkgPath string) bool {
switch pkgPath {
case "unsafe", "syscall", "runtime/cgo":
return true
func (p *context) pkgNoInit(pkg *types.Package) bool {
p.ensureLoaded(pkg)
if i, ok := p.loaded[pkg]; ok {
return i.kind >= PkgNoInit
}
return false
}
@@ -113,13 +113,22 @@ func inPkg(name, pkg string) bool {
// -----------------------------------------------------------------------------
type none = struct{}
type instrOrValue interface {
ssa.Instruction
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 {
prog llssa.Program
pkg llssa.Package
@@ -129,10 +138,11 @@ type context struct {
goTyps *types.Package
goPkg *ssa.Package
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
vargs map[*ssa.Alloc][]llssa.Expr // varargs
inits []func()
phis []func()
}
func (p *context) compileType(pkg llssa.Package, t *ssa.Type) {
@@ -159,7 +169,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
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)
p.compileFunc(pkg, mthd.Obj().Pkg(), ssaMthd, false)
}
}
}
@@ -167,32 +177,51 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
// Global variable.
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
typ := gbl.Type()
name := llssa.FullName(gbl.Pkg.Pkg, gbl.Name())
name, vtype := p.varName(gbl.Pkg.Pkg, gbl)
if ignoreName(name) || checkCgo(gbl.Name()) {
return
}
if debugInstr {
log.Println("==> NewVar", name, typ)
}
if vtype == cVar {
typ = llssa.CType(typ)
}
g := pkg.NewVar(name, typ)
if vtype == goVar {
g.Init(p.prog.Null(g.Type))
}
}
func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) {
sig := f.Signature
name, ok := p.funcName(pkgTypes, f, true)
if !ok { // ignored
return
func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function, closure bool) llssa.Function {
var sig = f.Signature
var name string
if closure {
name = funcName(pkgTypes, f)
if debugInstr {
log.Println("==> NewClosure", name, "type:", sig)
}
} else {
var ftype int
name, ftype = p.funcName(pkgTypes, f, true)
switch ftype {
case ignoredFunc, llgoInstr: // llgo extended instructions
return nil
}
if debugInstr {
log.Println("==> NewFunc", name, "type:", sig.Recv(), sig)
}
if ftype == cFunc {
sig = llssa.CFuncDecl(sig)
}
}
fn := pkg.NewFunc(name, sig)
p.inits = append(p.inits, func() {
p.fn = fn
defer func() {
p.fn = nil
}()
p.phis = nil
nblk := len(f.Blocks)
if nblk == 0 { // external function
return
@@ -209,22 +238,37 @@ func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa
for i, block := range f.Blocks {
p.compileBlock(b, block, i == 0 && name == "main")
}
for _, phi := range p.phis {
phi()
}
})
return fn
}
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bool) llssa.BasicBlock {
ret := p.fn.Block(block.Index)
b.SetBlock(ret)
if doInit {
fn := p.pkg.FuncOf("main.init")
b.Call(fn.Expr)
pkg := p.pkg
callRuntimeInit(b, pkg)
b.Call(pkg.FuncOf("main.init").Expr)
}
for _, instr := range block.Instrs {
instrs := p.compilePhis(b, block.Instrs)
for _, instr := range instrs {
p.compileInstr(b, instr)
}
return ret
}
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()
@@ -260,6 +304,90 @@ func (p *context) checkVArgs(v *ssa.Alloc, t *types.Pointer) bool {
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 (p *context) advance(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
if len(args) == 2 {
ptr := p.compileValue(b, args[0])
offset := p.compileValue(b, args[1])
return b.Advance(ptr, offset)
}
panic("advance(p ptr, offset int): invalid arguments")
}
// func alloca(size uintptr) unsafe.Pointer
func (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 isPhi(i ssa.Instruction) bool {
_, ok := i.(*ssa.Phi)
return ok
}
func (p *context) compilePhis(b llssa.Builder, instrs []ssa.Instruction) []ssa.Instruction {
if ninstr := len(instrs); ninstr > 0 {
if isPhi(instrs[0]) {
n := 1
for n < ninstr && isPhi(instrs[n]) {
n++
}
rets := make([]llssa.Expr, n)
for i := 0; i < n; i++ {
iv := instrs[i].(*ssa.Phi)
rets[i] = p.compilePhi(b, iv)
}
for i := 0; i < n; i++ {
iv := instrs[i].(*ssa.Phi)
p.bvals[iv] = rets[i].Do(b)
}
return instrs[n:]
}
}
return instrs
}
func (p *context) compilePhi(b llssa.Builder, v *ssa.Phi) (ret llssa.Expr) {
phi := b.Phi(p.prog.Type(v.Type()))
ret = phi.Expr
p.phis = append(p.phis, func() {
preds := v.Block().Preds
bblks := make([]llssa.BasicBlock, len(preds))
for i, pred := range preds {
bblks[i] = p.fn.Block(pred.Index)
}
edges := v.Edges
phi.AddIncoming(b, bblks, func(i int) llssa.Expr {
return p.compileValue(b, edges[i])
})
})
return
}
func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue bool) (ret llssa.Expr) {
if asValue {
if v, ok := p.bvals[iv]; ok {
@@ -271,15 +399,16 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
case *ssa.Call:
call := v.Call
cv := call.Value
kind := funcKind(cv)
kind := p.funcKind(cv)
if kind == fnIgnore {
return
}
if debugGoSSA {
log.Println(">>> Call", cv, call.Args)
}
if builtin, ok := cv.(*ssa.Builtin); ok {
fn := builtin.Name()
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)
@@ -288,7 +417,26 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
args := p.compileValues(b, call.Args, kind)
ret = b.BuiltinCall(fn, args...)
}
} else {
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 llgoAdvance:
ret = p.advance(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:
fn := p.compileValue(b, cv)
args := p.compileValues(b, call.Args, kind)
ret = b.Call(fn, args...)
@@ -311,6 +459,12 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
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
@@ -319,18 +473,34 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
x := p.compileValue(b, vx)
idx := p.compileValue(b, v.Index)
ret = b.IndexAddr(x, idx)
case *ssa.Index:
x := p.compileValue(b, v.X)
idx := p.compileValue(b, v.Index)
ret = b.Index(x, idx, func(e llssa.Expr) (ret llssa.Expr) {
if e == x {
if n, ok := v.X.(*ssa.UnOp); ok {
return p.compileValue(b, n.X)
}
}
panic(fmt.Errorf("todo addr of %v", e))
})
case *ssa.Slice:
vx := v.X
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs slice
return
}
panic("todo")
case *ssa.Alloc:
t := v.Type().(*types.Pointer)
if p.checkVArgs(v, t) { // varargs: this is a varargs allocation
return
var low, high, max llssa.Expr
x := p.compileValue(b, vx)
if v.Low != nil {
low = p.compileValue(b, v.Low)
}
ret = b.Alloc(t, v.Heap)
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.MakeInterface:
const (
delayExpr = true // varargs: don't need to convert an expr to any
@@ -338,6 +508,21 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
t := v.Type()
x := p.compileValue(b, v.X)
ret = b.MakeInterface(t, x, delayExpr)
case *ssa.MakeSlice:
var nCap llssa.Expr
t := v.Type()
nLen := p.compileValue(b, v.Len)
if v.Cap != nil {
nCap = p.compileValue(b, v.Cap)
}
ret = b.MakeSlice(p.prog.Type(t), nLen, nCap)
case *ssa.MakeMap:
var nReserve llssa.Expr
t := v.Type()
if v.Reserve != nil {
nReserve = p.compileValue(b, v.Reserve)
}
ret = b.MakeMap(p.prog.Type(t), nReserve)
case *ssa.TypeAssert:
x := p.compileValue(b, v.X)
ret = b.TypeAssert(x, p.prog.Type(v.AssertedType), v.CommaOk)
@@ -391,8 +576,13 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
thenb := fn.Block(succs[0].Index)
elseb := fn.Block(succs[1].Index)
b.If(cond, thenb, elseb)
case *ssa.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()
arg := p.compileValue(b, v.X).Do(b)
b.Panic(arg)
default:
panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr))
@@ -412,13 +602,20 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
}
}
case *ssa.Function:
fn := p.funcOf(v)
if v.Blocks != nil {
fn := p.compileFunc(p.pkg, p.goTyps, v, true)
return fn.Expr
}
fn, ftype := p.funcOf(v)
if ftype >= llgoInstrBase {
panic("can't use llgo instruction as a value")
}
return fn.Expr
case *ssa.Global:
g := p.varOf(v)
return g.Expr
case *ssa.Const:
t := v.Type()
t := types.Default(v.Type())
return b.Const(v.Value, p.prog.Type(t))
}
panic(fmt.Sprintf("compileValue: unknown value - %T\n", v))
@@ -443,7 +640,7 @@ func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int)
n := len(vals) - hasVArg
ret := make([]llssa.Expr, n)
for i := 0; i < n; i++ {
ret[i] = p.compileValue(b, vals[i]).Do()
ret[i] = p.compileValue(b, vals[i]).Do(b)
}
if hasVArg > 0 {
ret = p.compileVArg(ret, b, vals[n])
@@ -484,8 +681,10 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
goTyps: pkgTypes,
goPkg: pkg,
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)
for _, m := range members {
@@ -496,16 +695,20 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
// Do not try to build generic (non-instantiated) functions.
continue
}
ctx.compileFunc(ret, member.Pkg.Pkg, member)
ctx.compileFunc(ret, member.Pkg.Pkg, member, false)
case *ssa.Type:
ctx.compileType(ret, member)
case *ssa.Global:
ctx.compileGlobal(ret, member)
}
}
for _, ini := range ctx.inits {
for len(ctx.inits) > 0 {
inits := ctx.inits
ctx.inits = nil
for _, ini := range inits {
ini()
}
}
return
}

View File

@@ -28,8 +28,8 @@ func testCompile(t *testing.T, src, expected string) {
cltest.TestCompileEx(t, src, "foo.go", expected)
}
func TestFromTestcgo(t *testing.T) {
cltest.FromDir(t, "", "./_testcgo", true)
func TestFromTestrt(t *testing.T) {
cltest.FromDir(t, "", "./_testrt", true)
}
func TestFromTestdata(t *testing.T) {

View File

@@ -19,6 +19,7 @@ package cl
import (
"bytes"
"go/ast"
"go/constant"
"go/token"
"go/types"
"os"
@@ -43,11 +44,45 @@ func contentOf(m contentMap, file string) (lines contentLines, err error) {
return
}
func (p *context) importPkg(pkg *types.Package) {
// PkgKindOf returns the kind of a package.
func PkgKindOf(pkg *types.Package) int {
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
}
i.kind = kind
fset := p.fset
names := scope.Names()
contents := make(contentMap)
@@ -55,19 +90,14 @@ func (p *context) importPkg(pkg *types.Package) {
for _, name := range names {
if token.IsExported(name) {
obj := scope.Lookup(name)
if obj, ok := obj.(*types.Func); ok {
switch obj := obj.(type) {
case *types.Func:
if pos := obj.Pos(); pos != token.NoPos {
f := fset.File(pos)
if fp := f.Position(pos); fp.Line > 2 {
lines, err := contentOf(contents, fp.Filename)
if err != nil {
panic(err)
}
if i := fp.Line - 2; i < len(lines) {
line := string(lines[i])
p.initLinkname(pkgPath, line)
}
p.initLinknameByPos(fset, pos, pkgPath, contents, false)
}
case *types.Var:
if pos := obj.Pos(); pos != token.NoPos {
p.initLinknameByPos(fset, pos, pkgPath, contents, true)
}
}
}
@@ -77,21 +107,44 @@ func (p *context) importPkg(pkg *types.Package) {
func (p *context) initFiles(pkgPath string, files []*ast.File) {
for _, file := range files {
for _, decl := range file.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok {
switch decl := decl.(type) {
case *ast.FuncDecl:
if decl.Recv == nil {
if doc := decl.Doc; doc != nil {
if n := len(doc.List); n > 0 {
line := doc.List[n-1].Text
p.initLinkname(pkgPath, line)
}
p.initLinknameByDoc(decl.Doc, pkgPath, false)
}
case *ast.GenDecl:
if decl.Tok == token.VAR && len(decl.Specs) == 1 {
p.initLinknameByDoc(decl.Doc, pkgPath, true)
}
}
}
}
}
func (p *context) initLinkname(pkgPath, line string) {
func (p *context) initLinknameByDoc(doc *ast.CommentGroup, pkgPath string, isVar bool) {
if doc != nil {
if n := len(doc.List); n > 0 {
line := doc.List[n-1].Text
p.initLinkname(pkgPath, line, isVar)
}
}
}
func (p *context) initLinknameByPos(fset *token.FileSet, pos token.Pos, pkgPath string, contents contentMap, isVar bool) {
f := fset.File(pos)
if fp := f.Position(pos); fp.Line > 2 {
lines, err := contentOf(contents, fp.Filename)
if err != nil {
panic(err)
}
if i := fp.Line - 2; i < len(lines) {
line := string(lines[i])
p.initLinkname(pkgPath, line, isVar)
}
}
}
func (p *context) initLinkname(pkgPath, line string, isVar bool) {
const (
linkname = "//go:linkname "
)
@@ -99,9 +152,9 @@ func (p *context) initLinkname(pkgPath, line string) {
text := strings.TrimSpace(line[len(linkname):])
if idx := strings.IndexByte(text, ' '); idx > 0 {
link := strings.TrimLeft(text[idx+1:], " ")
if strings.Contains(link, ".") { // eg. C.printf, C.strlen
if isVar || strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr
name := pkgPath + "." + text[:idx]
p.link[name] = link[2:]
p.link[name] = link
} else {
panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf")
}
@@ -136,43 +189,105 @@ func checkCgo(fnName string) bool {
(fnName[4] == '_' || strings.HasPrefix(fnName[4:], "Check"))
}
func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (string, bool) {
const (
ignoredFunc = iota
goFunc
cFunc
llgoInstr = -1
llgoInstrBase = 0x80
llgoUnreachable = llgoInstrBase + 0
llgoCstr = llgoInstrBase + 1
llgoAlloca = llgoInstrBase + 2
llgoAllocaCStr = llgoInstrBase + 3
llgoAdvance = llgoInstrBase + 4
)
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, false
return name, ignoredFunc
}
if v, ok := p.link[name]; ok {
return v, true
if strings.HasPrefix(v, "C.") {
return v[2:], cFunc
}
return name, true
if strings.HasPrefix(v, "llgo.") {
return v[5:], llgoInstr
}
return v, goFunc
}
return name, goFunc
}
func (p *context) funcOf(fn *ssa.Function) llssa.Function {
const (
ignoredVar = iota
goVar
cVar
)
func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, vtype int) {
name := llssa.FullName(pkg, v.Name())
if v, ok := p.link[name]; ok {
return v, cVar
}
return name, goVar
}
// funcOf returns a function by name and set ftype = goFunc, cFunc, etc.
// or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc.
func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
pkgTypes := p.ensureLoaded(fn.Pkg.Pkg)
pkg := p.pkg
name, _ := p.funcName(pkgTypes, fn, false)
if ret := pkg.FuncOf(name); ret != nil {
return ret
name, ftype := p.funcName(pkgTypes, fn, false)
if ftype == llgoInstr {
switch name {
case "cstr":
ftype = llgoCstr
case "advance":
ftype = llgoAdvance
case "alloca":
ftype = llgoAlloca
case "allocaCStr":
ftype = llgoAllocaCStr
case "unreachable":
ftype = llgoUnreachable
default:
panic("unknown llgo instruction: " + name)
}
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) (ret llssa.Global) {
pkgTypes := p.ensureLoaded(v.Pkg.Pkg)
pkg := p.pkg
name := llssa.FullName(pkgTypes, v.Name())
if ret := pkg.VarOf(name); ret != nil {
return ret
name, _ := p.varName(pkgTypes, v)
if ret = pkg.VarOf(name); ret == nil {
ret = pkg.NewVar(name, v.Type())
}
return pkg.NewVar(name, v.Type())
return
}
func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package {
if p.goTyps != pkgTypes {
if _, ok := p.loaded[pkgTypes]; !ok {
p.loaded[pkgTypes] = none{}
p.importPkg(pkgTypes)
i := &pkgInfo{
kind: pkgKindByPath(pkgTypes.Path()),
}
p.loaded[pkgTypes] = i
p.importPkg(pkgTypes, i)
}
}
return pkgTypes
}
func pkgKindByPath(pkgPath string) int {
switch pkgPath {
case "syscall", "runtime/cgo", "unsafe":
return PkgDeclOnly
}
return PkgNormal
}

View File

@@ -4,7 +4,7 @@ import "C"
import _ "unsafe"
const (
LLGoPackage = true
LLGoPackage = "decl"
)
//go:linkname Printf C.printf

View File

@@ -0,0 +1,9 @@
package linktarget
import (
"github.com/goplus/llgo/internal/runtime/c"
)
func F(a, b *c.Char) {
c.Printf(c.Str("a: %s, b: %s\n"), a, b)
}

View File

@@ -0,0 +1,26 @@
; ModuleID = 'github.com/goplus/llgo/cl/internal/linktarget'
source_filename = "github.com/goplus/llgo/cl/internal/linktarget"
@"github.com/goplus/llgo/cl/internal/linktarget.init$guard" = global ptr null
@0 = private unnamed_addr constant [14 x i8] c"a: %s, b: %s\0A\00", align 1
define void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr %0, ptr %1) {
_llgo_0:
%2 = call i32 (ptr, ...) @printf(ptr @0, ptr %0, ptr %1)
ret void
}
define void @"github.com/goplus/llgo/cl/internal/linktarget.init"() {
_llgo_0:
%0 = load i1, ptr @"github.com/goplus/llgo/cl/internal/linktarget.init$guard", align 1
br i1 %0, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"github.com/goplus/llgo/cl/internal/linktarget.init$guard", align 1
br label %_llgo_2
_llgo_2: ; preds = %_llgo_1, %_llgo_0
ret void
}
declare i32 @printf(ptr, ...)

2
go.mod
View File

@@ -5,7 +5,7 @@ go 1.18
require (
github.com/aykevl/go-wasm v0.0.1
github.com/goplus/gogen v1.15.2
github.com/goplus/llvm v0.7.2
github.com/goplus/llvm v0.7.5
github.com/goplus/mod v0.13.10
github.com/qiniu/x v1.13.10
golang.org/x/tools v0.20.0

6
go.sum
View File

@@ -2,10 +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/goplus/gogen v1.15.2 h1:Q6XaSx/Zi5tWnjfAziYsQI6Jv6MgODRpFtOYqNkiiqM=
github.com/goplus/gogen v1.15.2/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk=
github.com/goplus/llvm v0.7.1 h1:B12Fr/wc3pAsq5PLuac9u9IuKpLRuCufdVAeGDP/MRw=
github.com/goplus/llvm v0.7.1/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
github.com/goplus/llvm v0.7.2 h1:NL3LlwAmYVCGA6yV40AjOvMDKl2dbCqoYPtugmLQK+E=
github.com/goplus/llvm v0.7.2/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
github.com/goplus/llvm v0.7.5 h1:ges8WcUdu4FBi0mkZUs27p/4qDQlj28N1UpMg3VQUoE=
github.com/goplus/llvm v0.7.5/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
github.com/goplus/mod v0.13.10 h1:5Om6KOvo31daN7N30kWU1vC5zhsJPM+uPbcEN/FnlzE=
github.com/goplus/mod v0.13.10/go.mod h1:HDuPZgpWiaTp3PUolFgsiX+Q77cbUWB/mikVHfYND3c=
github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE=

View File

@@ -2,13 +2,13 @@
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.Type" = type { i64, i64, i32, i8, i8, i8, i8, { ptr, ptr }, ptr, i32, i32 }
%"github.com/goplus/llgo/internal/abi.ChanType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, i64 }
%"github.com/goplus/llgo/internal/abi.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.MapType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr, ptr, ptr, { ptr, ptr }, i8, i8, i16, i32 }
%"github.com/goplus/llgo/internal/abi.PtrType" = type { %"github.com/goplus/llgo/internal/abi.Type", ptr }
%"github.com/goplus/llgo/internal/abi.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" }

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

View File

@@ -0,0 +1,32 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package aliases
import (
"go/token"
"go/types"
)
// Package aliases defines backward compatible shims
// for the types.Alias type representation added in 1.22.
// This defines placeholders for x/tools until 1.26.
// NewAlias creates a new TypeName in Package pkg that
// is an alias for the type rhs.
//
// The enabled parameter determines whether the resulting [TypeName]'s
// type is an [types.Alias]. Its value must be the result of a call to
// [Enabled], which computes the effective value of
// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled
// function is expensive and should be called once per task (e.g.
// package import), not once per call to NewAlias.
func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName {
if enabled {
tname := types.NewTypeName(pos, pkg, name, nil)
newAlias(tname, rhs)
return tname
}
return types.NewTypeName(pos, pkg, name, rhs)
}

View File

@@ -0,0 +1,31 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.22
// +build !go1.22
package aliases
import (
"go/types"
)
// Alias is a placeholder for a go/types.Alias for <=1.21.
// It will never be created by go/types.
type Alias struct{}
func (*Alias) String() string { panic("unreachable") }
func (*Alias) Underlying() types.Type { panic("unreachable") }
func (*Alias) Obj() *types.TypeName { panic("unreachable") }
func Rhs(alias *Alias) types.Type { panic("unreachable") }
// Unalias returns the type t for go <=1.21.
func Unalias(t types.Type) types.Type { return t }
func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") }
// Enabled reports whether [NewAlias] should create [types.Alias] types.
//
// Before go1.22, this function always returns false.
func Enabled() bool { return false }

View File

@@ -0,0 +1,63 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.22
// +build go1.22
package aliases
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
)
// Alias is an alias of types.Alias.
type Alias = types.Alias
// Rhs returns the type on the right-hand side of the alias declaration.
func Rhs(alias *Alias) types.Type {
if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok {
return alias.Rhs() // go1.23+
}
// go1.22's Alias didn't have the Rhs method,
// so Unalias is the best we can do.
return Unalias(alias)
}
// Unalias is a wrapper of types.Unalias.
func Unalias(t types.Type) types.Type { return types.Unalias(t) }
// newAlias is an internal alias around types.NewAlias.
// Direct usage is discouraged as the moment.
// Try to use NewAlias instead.
func newAlias(tname *types.TypeName, rhs types.Type) *Alias {
a := types.NewAlias(tname, rhs)
// TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect.
Unalias(a)
return a
}
// Enabled reports whether [NewAlias] should create [types.Alias] types.
//
// This function is expensive! Call it sparingly.
func Enabled() bool {
// The only reliable way to compute the answer is to invoke go/types.
// We don't parse the GODEBUG environment variable, because
// (a) it's tricky to do so in a manner that is consistent
// with the godebug package; in particular, a simple
// substring check is not good enough. The value is a
// rightmost-wins list of options. But more importantly:
// (b) it is impossible to detect changes to the effective
// setting caused by os.Setenv("GODEBUG"), as happens in
// many tests. Therefore any attempt to cache the result
// is just incorrect.
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
_, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias)
return enabled
}

View File

@@ -126,7 +126,7 @@ func Do(args []string, conf *Config) {
return rt[0].Types
})
buildAllPkgs(prog, initial, mode, verbose)
pkgs := buildAllPkgs(prog, initial, mode, verbose)
var runtimeFiles []string
if rt != nil {
@@ -136,12 +136,11 @@ func Do(args []string, conf *Config) {
nErr := 0
for _, pkg := range initial {
if pkg.Name == "main" {
nErr += linkMainPkg(pkg, runtimeFiles, conf, mode, verbose)
nErr += linkMainPkg(pkg, pkgs, runtimeFiles, conf, mode, verbose)
}
}
if nErr > 0 {
fmt.Fprintf(os.Stderr, "%d errors occurred\n", nErr)
os.Exit(1)
os.Exit(nErr)
}
}
}
@@ -154,11 +153,14 @@ func isNeedRuntime(pkg *packages.Package) bool {
return pkg.ID == ""
}
func buildAllPkgs(prog llssa.Program, initial []*packages.Package, mode Mode, verbose bool) {
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 {
@@ -167,9 +169,10 @@ func buildAllPkgs(prog llssa.Program, initial []*packages.Package, mode Mode, ve
setNeedRuntime(pkg.Package)
}
}
return
}
func linkMainPkg(pkg *packages.Package, runtimeFiles []string, conf *Config, mode Mode, verbose bool) (nErr int) {
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
@@ -183,7 +186,7 @@ func linkMainPkg(pkg *packages.Package, runtimeFiles []string, conf *Config, mod
args[2] = "-Wno-override-module"
needRuntime := false
packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) {
if p.PkgPath != "unsafe" { // TODO(xsw): maybe can remove this special case
if p.ExportFile != "" && !isRuntimePkg(p.PkgPath) { // skip packages that only contain declarations
args = append(args, p.ExportFile+".ll")
if !needRuntime {
needRuntime = isNeedRuntime(p)
@@ -192,6 +195,17 @@ func linkMainPkg(pkg *packages.Package, runtimeFiles []string, conf *Config, mod
})
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 {
@@ -204,7 +218,9 @@ func linkMainPkg(pkg *packages.Package, runtimeFiles []string, conf *Config, mod
}()
// TODO(xsw): show work
// fmt.Fprintln(os.Stderr, "clang", args)
if verbose {
fmt.Fprintln(os.Stderr, "clang", args)
}
err := clang.New("").Exec(args...)
check(err)
@@ -218,8 +234,14 @@ func linkMainPkg(pkg *packages.Package, runtimeFiles []string, conf *Config, mod
return
}
func buildPkg(prog llssa.Program, aPkg aPackage, mode Mode, verbose bool) {
func buildPkg(prog llssa.Program, aPkg *aPackage, mode Mode, verbose bool) {
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
if verbose {
fmt.Fprintln(os.Stderr, pkgPath)
@@ -233,14 +255,16 @@ func buildPkg(prog llssa.Program, aPkg aPackage, mode Mode, verbose bool) {
file := pkg.ExportFile + ".ll"
os.WriteFile(file, []byte(ret.String()), 0644)
}
aPkg.LPkg = ret
}
type aPackage struct {
*packages.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
if len(initial) > 0 {
fset = initial[0].Fset
@@ -250,7 +274,7 @@ func allPkgs(initial []*packages.Package, mode ssa.BuilderMode) (prog *ssa.Progr
packages.Visit(initial, nil, func(p *packages.Package) {
if p.Types != nil && !p.IllTyped {
ssaPkg := prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true)
all = append(all, aPackage{p, ssaPkg})
all = append(all, &aPackage{p, ssaPkg, nil})
} else {
errs = append(errs, p)
}
@@ -320,14 +344,28 @@ func allLinkFiles(rt []*packages.Package) (outFiles []string) {
outFiles = make([]string, 0, len(rt))
root := rootLLGo(rt[0])
packages.Visit(rt, nil, func(p *packages.Package) {
if isPkgInLLGo(p.PkgPath) {
outFile := filepath.Join(root+p.PkgPath[len(llgoModPath):], "llgo_autogen.ll")
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
@@ -337,6 +375,7 @@ const (
llgoModPath = "github.com/goplus/llgo"
)
/*
func isPkgInLLGo(pkgPath string) bool {
return isPkgInMod(pkgPath, llgoModPath)
}
@@ -348,6 +387,7 @@ func isPkgInMod(pkgPath, modPath string) bool {
}
return false
}
*/
func check(err error) {
if err != nil {

View File

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

View File

@@ -19,6 +19,8 @@ package llgen
import (
"go/types"
"os"
"path/filepath"
"strings"
"github.com/goplus/llgo/cl"
"golang.org/x/tools/go/packages"
@@ -37,7 +39,7 @@ const (
func GenFrom(fileOrPkg string) string {
cfg := &packages.Config{
Mode: loadSyntax,
Mode: loadSyntax | packages.NeedDeps,
}
initial, err := packages.Load(cfg, fileOrPkg)
check(err)
@@ -55,6 +57,10 @@ func GenFrom(fileOrPkg string) string {
return rt[0].Types
})
if Verbose {
ssaPkg.WriteTo(os.Stderr)
}
ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax)
check(err)
@@ -66,3 +72,23 @@ func DoFile(fileOrPkg, outFile string) {
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/")
}

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

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package c
import "C"
import "unsafe"
const (
LLGoPackage = "decl"
)
type (
Char = int8
Int = C.int
Pointer = unsafe.Pointer
FilePtr = unsafe.Pointer
)
//go:linkname Stdin __stdinp
var Stdin FilePtr
//go:linkname Stdout __stdoutp
var Stdout FilePtr
//go:linkname Stderr __stderrp
var Stderr FilePtr
//go:linkname Str llgo.cstr
func Str(string) *Char
//go:linkname Advance llgo.advance
func Advance(ptr Pointer, offset int) Pointer
//go:linkname Alloca llgo.alloca
func Alloca(size uintptr) Pointer
//go:linkname AllocaCStr llgo.allocaCStr
func AllocaCStr(s string) *Char
//go:linkname Unreachable llgo.unreachable
func Unreachable()
//go:linkname Rand C.rand
func Rand() Int
//go:linkname Malloc C.malloc
func Malloc(size uintptr) Pointer
//go:linkname Memcpy C.memcpy
func Memcpy(dst, src Pointer, n uintptr) Pointer
//go:linkname Memset C.memset
func Memset(s Pointer, c Int, n uintptr) Pointer
//go:linkname Printf C.printf
func Printf(format *Char, __llgo_va_list ...any) Int
//go:linkname Fprintf C.fprintf
func Fprintf(fp FilePtr, format *Char, __llgo_va_list ...any) Int

View File

@@ -1,23 +1,36 @@
; 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.String" = type { ptr, i64 }
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, { ptr, ptr }, ptr, i32, i32 }
%"github.com/goplus/llgo/internal/runtime.hmap" = type { i64, i8, i8, i16, i32, ptr, ptr, i64, ptr }
@"github.com/goplus/llgo/internal/runtime.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 [26 x i8] c"slice index out of bounds\00", align 1
@2 = private unnamed_addr constant [33 x i8] c"string slice index out of bounds\00", align 1
@__stderrp = external global ptr
@3 = private unnamed_addr constant [11 x i8] c"panic: %s\0A\00", align 1
define ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 %0) {
define ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 %0) {
_llgo_0:
%1 = call ptr @malloc(i64 %0)
ret ptr %1
}
define ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 %0) {
_llgo_0:
%1 = call ptr @malloc(i64 %0)
%2 = call ptr @memset(ptr %1, i32 0, i64 %0)
ret ptr %2
}
define ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 %0) {
_llgo_0:
%1 = getelementptr inbounds ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 %0
@@ -25,22 +38,52 @@ _llgo_0:
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
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %1, ptr %3, align 8
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
%5 = load i64, ptr %4, align 4
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 0
%7 = load ptr, ptr %6, align 8
%8 = call ptr @memcpy(ptr %0, ptr %7, i64 %5)
%9 = getelementptr inbounds i8, ptr %0, i64 %5
store i8 0, ptr %9, 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
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %0, 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 = add i64 %4, 1
%6 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 %5)
%7 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %2, align 8
%8 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %6, %"github.com/goplus/llgo/internal/runtime.String" %7)
ret ptr %8
}
define { i64, i1 } @"github.com/goplus/llgo/internal/runtime.CheckI2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
_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
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %3, align 8
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 0
%5 = load ptr, ptr %4, align 8
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 1
%7 = load ptr, ptr %6, align 8
%8 = icmp eq ptr %7, %1
br i1 %8, 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
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 1
%10 = load ptr, ptr %9, align 8
%11 = ptrtoint ptr %10 to i64
%mrv = insertvalue { i64, i1 } poison, i64 %11, 0
%mrv1 = insertvalue { i64, i1 } %mrv, i1 true, 1
ret { i64, i1 } %mrv1
@@ -51,155 +94,427 @@ _llgo_2: ; preds = %_llgo_0
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
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 16)
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 0
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 1
store ptr null, ptr %2, align 8
store i64 0, ptr %3, align 4
%4 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %1, align 8
ret %"github.com/goplus/llgo/internal/runtime.String" %4
}
define i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1) {
_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
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %3, align 8
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 0
%5 = load ptr, ptr %4, align 8
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 1
%7 = load ptr, ptr %6, align 8
%8 = icmp eq ptr %7, %1
br i1 %8, 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
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 1
%10 = load ptr, ptr %9, align 8
%11 = ptrtoint ptr %10 to i64
ret i64 %11
_llgo_2: ; preds = %_llgo_0
%11 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"([21 x i8] c"I2Int: type mismatch\00")
%12 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 20)
%13 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %12)
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %13)
unreachable
}
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAny"(ptr %0, ptr %1) {
_llgo_0:
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 0
%6 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 2
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 4
%10 = getelementptr inbounds i64, ptr %9, i64 0
store ptr %6, ptr %5, align 8
store ptr %0, ptr %7, align 8
store i32 0, ptr %8, align 4
store i64 0, ptr %10, align 4
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 0
%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 %1, ptr %11, align 8
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, align 8
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
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 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %9, i64 16)
%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 %2, 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.MakeAnyInt"(ptr %0, i64 %1) {
_llgo_0:
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 0
%6 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 2
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 4
%10 = getelementptr inbounds i64, ptr %9, i64 0
store ptr %6, ptr %5, align 8
store ptr %0, ptr %7, align 8
store i32 0, ptr %8, align 4
store i64 0, ptr %10, align 4
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
%12 = inttoptr i64 %1 to ptr
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 0
%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 %12, ptr %11, align 8
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, align 8
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
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 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %9, i64 16)
%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
%13 = inttoptr i64 %1 to ptr
store ptr %2, ptr %11, align 8
store ptr %13, ptr %12, align 8
%14 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, align 8
ret %"github.com/goplus/llgo/internal/runtime.iface" %14
}
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %1, align 8
%2 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 0
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 0
%6 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 1
%8 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 24)
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 2
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %4, i32 0, i32 4
%11 = getelementptr inbounds i64, ptr %10, i64 0
store ptr %6, ptr %5, align 8
store ptr %8, ptr %7, align 8
store i32 0, ptr %9, align 4
store i64 0, ptr %11, align 4
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
store ptr %4, ptr %3, align 8
store ptr %1, ptr %12, align 8
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, align 8
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
%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.AllocZ"(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 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %10, i64 16)
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %11, i32 0, i32 0
%13 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %11, i32 0, i32 1
store ptr %3, ptr %12, align 8
store ptr %1, ptr %13, align 8
%14 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %11, align 8
ret %"github.com/goplus/llgo/internal/runtime.iface" %14
}
define %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeInterface"(ptr %0, ptr %1, ptr %2) {
_llgo_0:
%3 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 0
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 0
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 1
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 2
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %5, i32 0, i32 4
%10 = getelementptr inbounds i64, ptr %9, i64 0
store ptr %0, ptr %6, align 8
store ptr %1, ptr %7, align 8
store i32 0, ptr %8, align 4
store i64 0, ptr %10, align 4
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, i32 0, i32 1
store ptr %5, ptr %4, align 8
store ptr %2, ptr %11, align 8
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %3, align 8
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 0
%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 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %9, i64 16)
%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 %2, 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
}
declare ptr @malloc(i64)
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 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %3, i64 24)
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %4, i32 0, i32 0
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %4, i32 0, i32 1
%7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %4, i32 0, i32 2
store ptr %0, ptr %5, align 8
store i64 %1, ptr %6, align 4
store i64 %2, ptr %7, align 4
%8 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %4, align 8
ret %"github.com/goplus/llgo/internal/runtime.Slice" %8
}
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %0, i64 %1, i64 %2, i64 %3, i64 %4, i64 %5) {
_llgo_0:
%6 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %6, i64 24)
%8 = icmp slt i64 %3, 0
br i1 %8, label %_llgo_1, label %_llgo_5
_llgo_1: ; preds = %_llgo_5, %_llgo_4, %_llgo_3, %_llgo_0
%9 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 25)
%10 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %9)
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %10)
unreachable
_llgo_2: ; preds = %_llgo_3
%11 = sub i64 %4, %3
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, i32 0, i32 1
store i64 %11, ptr %12, align 4
%13 = sub i64 %5, %3
%14 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, i32 0, i32 2
store i64 %13, ptr %14, align 4
%15 = sub i64 %5, %3
%16 = icmp sgt i64 %15, 0
br i1 %16, label %_llgo_6, label %_llgo_8
_llgo_3: ; preds = %_llgo_4
%17 = icmp sgt i64 %5, %2
br i1 %17, label %_llgo_1, label %_llgo_2
_llgo_4: ; preds = %_llgo_5
%18 = icmp slt i64 %5, %4
br i1 %18, label %_llgo_1, label %_llgo_3
_llgo_5: ; preds = %_llgo_0
%19 = icmp slt i64 %4, %3
br i1 %19, label %_llgo_1, label %_llgo_4
_llgo_6: ; preds = %_llgo_2
%20 = mul i64 %3, %1
%21 = getelementptr i8, ptr %0, i64 %20
%22 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, i32 0, i32 0
store ptr %21, ptr %22, align 8
br label %_llgo_7
_llgo_7: ; preds = %_llgo_8, %_llgo_6
%23 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, align 8
ret %"github.com/goplus/llgo/internal/runtime.Slice" %23
_llgo_8: ; preds = %_llgo_2
%24 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %7, i32 0, i32 0
store ptr %0, ptr %24, align 8
br label %_llgo_7
}
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr %0, i64 %1) {
_llgo_0:
%2 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 0
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
store ptr %0, ptr %4, align 8
store i64 %1, ptr %5, align 4
%6 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %3, align 8
ret %"github.com/goplus/llgo/internal/runtime.String" %6
}
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewStringSlice"(%"github.com/goplus/llgo/internal/runtime.String" %0, i64 %1, i64 %2) {
_llgo_0:
%3 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %3, i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %4, align 8
%5 = icmp slt i64 %1, 0
br i1 %5, label %_llgo_1, label %_llgo_4
_llgo_1: ; preds = %_llgo_4, %_llgo_3, %_llgo_0
%6 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 32)
%7 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %6)
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %7)
unreachable
_llgo_2: ; preds = %_llgo_3
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 1
%9 = load i64, ptr %8, align 4
%10 = icmp slt i64 %1, %9
br i1 %10, label %_llgo_5, label %_llgo_6
_llgo_3: ; preds = %_llgo_4
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 1
%12 = load i64, ptr %11, align 4
%13 = icmp sgt i64 %2, %12
br i1 %13, label %_llgo_1, label %_llgo_2
_llgo_4: ; preds = %_llgo_0
%14 = icmp slt i64 %2, %1
br i1 %14, label %_llgo_1, label %_llgo_3
_llgo_5: ; preds = %_llgo_2
%15 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%16 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %15, i64 16)
%17 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %16, i32 0, i32 0
%18 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 0
%19 = load ptr, ptr %18, align 8
%20 = getelementptr i8, ptr %19, i64 %1
%21 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %16, i32 0, i32 1
%22 = sub i64 %2, %1
store ptr %20, ptr %17, align 8
store i64 %22, ptr %21, align 4
%23 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %16, align 8
ret %"github.com/goplus/llgo/internal/runtime.String" %23
_llgo_6: ; preds = %_llgo_2
%24 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%25 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %24, i64 16)
%26 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %25, i32 0, i32 0
%27 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %25, i32 0, i32 1
store ptr null, ptr %26, align 8
store i64 0, ptr %27, align 4
%28 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %25, align 8
ret %"github.com/goplus/llgo/internal/runtime.String" %28
}
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NilSlice"() {
_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
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 24)
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 0
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 1
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 2
store ptr null, ptr %2, align 8
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
store i64 0, ptr %4, align 4
%5 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, align 8
ret %"github.com/goplus/llgo/internal/runtime.Slice" %5
}
define i64 @"github.com/goplus/llgo/internal/runtime.SliceCap"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 24)
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %2, align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %2, i32 0, i32 2
%4 = load i64, ptr %3, align 4
ret i64 %4
}
define ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 24)
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %2, align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %2, i32 0, i32 0
%4 = load ptr, ptr %3, align 8
ret ptr %4
}
define i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
_llgo_0:
%1 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 24)
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %2, align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %2, i32 0, i32 1
%4 = load i64, ptr %3, align 4
ret i64 %4
}
define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String" %0, %"github.com/goplus/llgo/internal/runtime.String" %1) {
_llgo_0:
%2 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %3, align 8
%4 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %4, i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %1, ptr %5, align 8
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
%7 = load i64, ptr %6, align 4
%8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %5, i32 0, i32 1
%9 = load i64, ptr %8, align 4
%10 = add i64 %7, %9
%11 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 %10)
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 0
%13 = load ptr, ptr %12, align 8
%14 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
%15 = load i64, ptr %14, align 4
%16 = call ptr @memcpy(ptr %11, ptr %13, i64 %15)
%17 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %3, i32 0, i32 1
%18 = load i64, ptr %17, align 4
%19 = getelementptr i8, ptr %11, i64 %18
%20 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %5, i32 0, i32 0
%21 = load ptr, ptr %20, align 8
%22 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %5, i32 0, i32 1
%23 = load i64, ptr %22, align 4
%24 = call ptr @memcpy(ptr %19, ptr %21, i64 %23)
%25 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%26 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %25, i64 16)
%27 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %26, i32 0, i32 0
%28 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %26, i32 0, i32 1
store ptr %11, ptr %27, align 8
store i64 %10, ptr %28, align 4
%29 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %26, align 8
ret %"github.com/goplus/llgo/internal/runtime.String" %29
}
define ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0:
%1 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %2, align 8
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0
%4 = load ptr, ptr %3, align 8
ret ptr %4
}
define i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %0) {
_llgo_0:
%1 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %0, 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
ret i64 %4
}
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
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %1, i64 16)
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 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %6, i32 0, i32 6
%8 = load i8, ptr %7, align 1
%9 = sext i8 %8 to i64
%10 = icmp eq i64 %9, 24
br i1 %10, label %_llgo_2, label %_llgo_1
_llgo_1: ; preds = %_llgo_2, %_llgo_0
ret void
_llgo_2: ; preds = %_llgo_0
%11 = load ptr, ptr @__stderrp, align 8
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i32 0, i32 1
%13 = load ptr, ptr %12, align 8
%14 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %13, align 8
call void @"github.com/goplus/llgo/internal/runtime.stringTracef"(ptr %11, ptr @3, %"github.com/goplus/llgo/internal/runtime.String" %14)
br label %_llgo_1
}
define ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 %1) {
_llgo_0:
%2 = call ptr @memset(ptr %0, i32 0, i64 %1)
ret ptr %2
}
define ptr @"github.com/goplus/llgo/internal/runtime.basicType"(i64 %0) {
_llgo_0:
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 56)
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 0
%3 = getelementptr inbounds i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 %0
%4 = load i64, ptr %3, align 4
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 6
%6 = trunc i64 %0 to i8
%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 i8 %6, ptr %5, align 1
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
@@ -208,7 +523,7 @@ _llgo_0:
_llgo_1: ; preds = %_llgo_0
store i1 true, ptr @"github.com/goplus/llgo/internal/runtime.init$guard", align 1
call void @"github.com/goplus/llgo/internal/abi.init"()
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 16)
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 88)
store ptr %1, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
store 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
@@ -267,4 +582,42 @@ _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.AllocZ"(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
}
define void @"github.com/goplus/llgo/internal/runtime.stringTracef"(ptr %0, ptr %1, %"github.com/goplus/llgo/internal/runtime.String" %2) {
_llgo_0:
%3 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
%4 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %3, i64 16)
store %"github.com/goplus/llgo/internal/runtime.String" %2, ptr %4, align 8
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %4, i32 0, i32 1
%6 = load i64, ptr %5, align 4
%7 = add i64 %6, 1
%8 = alloca i8, i64 %7, align 1
%9 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %4, align 8
%10 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %8, %"github.com/goplus/llgo/internal/runtime.String" %9)
%11 = call i32 (ptr, ptr, ...) @fprintf(ptr %0, ptr %1, ptr %10)
ret void
}
declare ptr @malloc(i64)
declare ptr @memset(ptr, i32, i64)
declare ptr @memcpy(ptr, ptr, i64)
declare void @"github.com/goplus/llgo/internal/abi.init"()
declare i32 @fprintf(ptr, ptr, ...)

1726
internal/runtime/map.go Normal file

File diff suppressed because it is too large Load Diff

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

View File

@@ -16,3 +16,21 @@ import (
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
*/

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

@@ -0,0 +1,55 @@
/*
* 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"
)
// AllocU allocates uninitialized memory.
func AllocU(size uintptr) unsafe.Pointer {
return c.Malloc(size)
}
// AllocZ allocates zero-initialized memory.
func AllocZ(size uintptr) unsafe.Pointer {
ret := c.Malloc(size)
return c.Memset(ret, 0, size)
}
// Zeroinit initializes memory to zero.
func Zeroinit(p c.Pointer, size uintptr) c.Pointer {
return c.Memset(p, 0, size)
}
// TracePanic prints panic message.
func TracePanic(v Interface) {
kind := abi.Kind(v.tab._type.Kind_)
switch {
case kind == abi.String:
stringTracef(c.Stderr, c.Str("panic: %s\n"), *(*String)(v.data))
}
// TODO(xsw): other message type
}
func stringTracef(fp c.FilePtr, format *c.Char, s String) {
cs := c.Alloca(uintptr(s.len) + 1)
c.Fprintf(fp, format, CStrCopy(cs, s))
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package runtime
/*
import (
"unsafe"
)
// Closure represents a closure.
type Closure struct {
f unsafe.Pointer
data unsafe.Pointer // means no context if data is nil
}
// NewClosure creates a closure.
func NewClosure(f, data unsafe.Pointer) Closure {
return Closure{f, data}
}
// ClosureF returns the function of a closure.
func ClosureF(c Closure) unsafe.Pointer {
return c.f
}
// ClosureData returns the data of a closure.
func ClosureData(c Closure) unsafe.Pointer {
return c.data
}
*/

View File

@@ -22,7 +22,7 @@ import (
"github.com/goplus/llgo/internal/abi"
)
type Interface = iface
// -----------------------------------------------------------------------------
type InterfaceType = abi.InterfaceType
@@ -30,31 +30,36 @@ 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: &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}},
data: unsafe.Pointer(data),
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: &itab{inter: TyAny, _type: Basic(abi.String), hash: 0, fun: [1]uintptr{0}},
data: unsafe.Pointer(&data),
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: &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}},
data: data,
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: &itab{inter: inter, _type: typ, hash: 0, fun: [1]uintptr{0}},
data: data,
tab: tab, data: data,
}
}
@@ -71,3 +76,5 @@ func CheckI2Int(v Interface, t *Type) (uintptr, bool) {
}
return 0, false
}
// -----------------------------------------------------------------------------

View File

@@ -16,16 +16,10 @@
package runtime
import "unsafe"
// Map represents a Go map.
type Map = hmap
const (
LLGoPackage = true
)
//go:linkname Malloc C.malloc
func Malloc(size uintptr) unsafe.Pointer
// Alloc allocates memory.
func Alloc(size uintptr) unsafe.Pointer {
return Malloc(size)
// MakeSmallMap creates a new small map.
func MakeSmallMap() *Map {
return makemap_small()
}

View File

@@ -18,11 +18,15 @@ package runtime
import (
"unsafe"
"github.com/goplus/llgo/internal/runtime/c"
)
// -----------------------------------------------------------------------------
// Slice is the runtime representation of a slice.
type Slice struct {
array unsafe.Pointer
data unsafe.Pointer
len int
cap int
}
@@ -31,3 +35,39 @@ type Slice struct {
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}
}
func NewSlice3(base unsafe.Pointer, eltSize, cap, i, j, k int) (s Slice) {
if i < 0 || j < i || k < j || k > cap {
panic("slice index out of bounds")
}
s.len = j - i
s.cap = k - i
if k-i > 0 {
s.data = c.Advance(base, i*eltSize)
} else {
s.data = base
}
return
}
// SliceLen returns the length of a slice.
func SliceLen(s Slice) int {
return s.len
}
// SliceCap returns the capacity of a slice.
func SliceCap(s Slice) int {
return s.cap
}
// SliceData returns the data pointer of a slice.
func SliceData(s Slice) unsafe.Pointer {
return s.data
}
// -----------------------------------------------------------------------------

View File

@@ -18,8 +18,12 @@ 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.
@@ -35,3 +39,55 @@ type String struct {
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 String) int {
return s.len
}
// StringData returns the data pointer of a string.
func StringData(s String) unsafe.Pointer {
return s.data
}
// StringCat concatenates two strings.
func StringCat(a, b String) String {
n := a.len + b.len
dest := AllocU(uintptr(n))
c.Memcpy(dest, a.data, uintptr(a.len))
c.Memcpy(c.Advance(dest, a.len), b.data, uintptr(b.len))
return String{dest, n}
}
// -----------------------------------------------------------------------------
// CStrCopy copies a Go string to a C string buffer and returns it.
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 := AllocU(uintptr(s.len + 1))
return CStrCopy(dest, s)
}
func NewStringSlice(base String, i, j int) String {
if i < 0 || j < i || j > base.len {
panic("string slice index out of bounds")
}
if i < base.len {
return String{c.Advance(base.data, i), j - i}
}
return String{nil, 0}
}
// -----------------------------------------------------------------------------

View File

@@ -22,6 +22,8 @@ import (
"github.com/goplus/llgo/internal/abi"
)
// -----------------------------------------------------------------------------
type Kind = abi.Kind
type Type = abi.Type
@@ -74,8 +76,11 @@ var (
)
func basicType(kind abi.Kind) *Type {
return &abi.Type{
return &Type{
Size_: sizeBasicTypes[kind],
Hash: uint32(kind),
Kind_: uint8(kind),
}
}
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,218 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typeparams
import (
"errors"
"fmt"
"go/types"
"os"
"strings"
)
//go:generate go run copytermlist.go
const debug = false
var ErrEmptyTypeSet = errors.New("empty type set")
// StructuralTerms returns a slice of terms representing the normalized
// structural type restrictions of a type parameter, if any.
//
// Structural type restrictions of a type parameter are created via
// non-interface types embedded in its constraint interface (directly, or via a
// chain of interface embeddings). For example, in the declaration
//
// type T[P interface{~int; m()}] int
//
// the structural restriction of the type parameter P is ~int.
//
// With interface embedding and unions, the specification of structural type
// restrictions may be arbitrarily complex. For example, consider the
// following:
//
// type A interface{ ~string|~[]byte }
//
// type B interface{ int|string }
//
// type C interface { ~string|~int }
//
// type T[P interface{ A|B; C }] int
//
// In this example, the structural type restriction of P is ~string|int: A|B
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
// which when intersected with C (~string|~int) yields ~string|int.
//
// StructuralTerms computes these expansions and reductions, producing a
// "normalized" form of the embeddings. A structural restriction is normalized
// if it is a single union containing no interface terms, and is minimal in the
// sense that removing any term changes the set of types satisfying the
// constraint. It is left as a proof for the reader that, modulo sorting, there
// is exactly one such normalized form.
//
// Because the minimal representation always takes this form, StructuralTerms
// returns a slice of tilde terms corresponding to the terms of the union in
// the normalized structural restriction. An error is returned if the
// constraint interface is invalid, exceeds complexity bounds, or has an empty
// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet.
//
// StructuralTerms makes no guarantees about the order of terms, except that it
// is deterministic.
func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) {
constraint := tparam.Constraint()
if constraint == nil {
return nil, fmt.Errorf("%s has nil constraint", tparam)
}
iface, _ := constraint.Underlying().(*types.Interface)
if iface == nil {
return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying())
}
return InterfaceTermSet(iface)
}
// InterfaceTermSet computes the normalized terms for a constraint interface,
// returning an error if the term set cannot be computed or is empty. In the
// latter case, the error will be ErrEmptyTypeSet.
//
// See the documentation of StructuralTerms for more information on
// normalization.
func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) {
return computeTermSet(iface)
}
// UnionTermSet computes the normalized terms for a union, returning an error
// if the term set cannot be computed or is empty. In the latter case, the
// error will be ErrEmptyTypeSet.
//
// See the documentation of StructuralTerms for more information on
// normalization.
func UnionTermSet(union *types.Union) ([]*types.Term, error) {
return computeTermSet(union)
}
func computeTermSet(typ types.Type) ([]*types.Term, error) {
tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0)
if err != nil {
return nil, err
}
if tset.terms.isEmpty() {
return nil, ErrEmptyTypeSet
}
if tset.terms.isAll() {
return nil, nil
}
var terms []*types.Term
for _, term := range tset.terms {
terms = append(terms, types.NewTerm(term.tilde, term.typ))
}
return terms, nil
}
// A termSet holds the normalized set of terms for a given type.
//
// The name termSet is intentionally distinct from 'type set': a type set is
// all types that implement a type (and includes method restrictions), whereas
// a term set just represents the structural restrictions on a type.
type termSet struct {
complete bool
terms termlist
}
func indentf(depth int, format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...)
}
func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
if t == nil {
panic("nil type")
}
if debug {
indentf(depth, "%s", t.String())
defer func() {
if err != nil {
indentf(depth, "=> %s", err)
} else {
indentf(depth, "=> %s", res.terms.String())
}
}()
}
const maxTermCount = 100
if tset, ok := seen[t]; ok {
if !tset.complete {
return nil, fmt.Errorf("cycle detected in the declaration of %s", t)
}
return tset, nil
}
// Mark the current type as seen to avoid infinite recursion.
tset := new(termSet)
defer func() {
tset.complete = true
}()
seen[t] = tset
switch u := t.Underlying().(type) {
case *types.Interface:
// The term set of an interface is the intersection of the term sets of its
// embedded types.
tset.terms = allTermlist
for i := 0; i < u.NumEmbeddeds(); i++ {
embedded := u.EmbeddedType(i)
if _, ok := embedded.Underlying().(*types.TypeParam); ok {
return nil, fmt.Errorf("invalid embedded type %T", embedded)
}
tset2, err := computeTermSetInternal(embedded, seen, depth+1)
if err != nil {
return nil, err
}
tset.terms = tset.terms.intersect(tset2.terms)
}
case *types.Union:
// The term set of a union is the union of term sets of its terms.
tset.terms = nil
for i := 0; i < u.Len(); i++ {
t := u.Term(i)
var terms termlist
switch t.Type().Underlying().(type) {
case *types.Interface:
tset2, err := computeTermSetInternal(t.Type(), seen, depth+1)
if err != nil {
return nil, err
}
terms = tset2.terms
case *types.TypeParam, *types.Union:
// A stand-alone type parameter or union is not permitted as union
// term.
return nil, fmt.Errorf("invalid union term %T", t)
default:
if t.Type() == types.Typ[types.Invalid] {
continue
}
terms = termlist{{t.Tilde(), t.Type()}}
}
tset.terms = tset.terms.union(terms)
if len(tset.terms) > maxTermCount {
return nil, fmt.Errorf("exceeded max term count %d", maxTermCount)
}
}
case *types.TypeParam:
panic("unreachable")
default:
// For all other types, the term set is just a single non-tilde term
// holding the type itself.
if u != types.Typ[types.Invalid] {
tset.terms = termlist{{false, t}}
}
}
return tset, nil
}
// under is a facade for the go/types internal function of the same name. It is
// used by typeterm.go.
func under(t types.Type) types.Type {
return t.Underlying()
}

View File

@@ -0,0 +1,163 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by copytermlist.go DO NOT EDIT.
package typeparams
import (
"bytes"
"go/types"
)
// A termlist represents the type set represented by the union
// t1 y2 ... tn of the type sets of the terms t1 to tn.
// A termlist is in normal form if all terms are disjoint.
// termlist operations don't require the operands to be in
// normal form.
type termlist []*term
// allTermlist represents the set of all types.
// It is in normal form.
var allTermlist = termlist{new(term)}
// String prints the termlist exactly (without normalization).
func (xl termlist) String() string {
if len(xl) == 0 {
return "∅"
}
var buf bytes.Buffer
for i, x := range xl {
if i > 0 {
buf.WriteString(" | ")
}
buf.WriteString(x.String())
}
return buf.String()
}
// isEmpty reports whether the termlist xl represents the empty set of types.
func (xl termlist) isEmpty() bool {
// If there's a non-nil term, the entire list is not empty.
// If the termlist is in normal form, this requires at most
// one iteration.
for _, x := range xl {
if x != nil {
return false
}
}
return true
}
// isAll reports whether the termlist xl represents the set of all types.
func (xl termlist) isAll() bool {
// If there's a 𝓤 term, the entire list is 𝓤.
// If the termlist is in normal form, this requires at most
// one iteration.
for _, x := range xl {
if x != nil && x.typ == nil {
return true
}
}
return false
}
// norm returns the normal form of xl.
func (xl termlist) norm() termlist {
// Quadratic algorithm, but good enough for now.
// TODO(gri) fix asymptotic performance
used := make([]bool, len(xl))
var rl termlist
for i, xi := range xl {
if xi == nil || used[i] {
continue
}
for j := i + 1; j < len(xl); j++ {
xj := xl[j]
if xj == nil || used[j] {
continue
}
if u1, u2 := xi.union(xj); u2 == nil {
// If we encounter a 𝓤 term, the entire list is 𝓤.
// Exit early.
// (Note that this is not just an optimization;
// if we continue, we may end up with a 𝓤 term
// and other terms and the result would not be
// in normal form.)
if u1.typ == nil {
return allTermlist
}
xi = u1
used[j] = true // xj is now unioned into xi - ignore it in future iterations
}
}
rl = append(rl, xi)
}
return rl
}
// union returns the union xl yl.
func (xl termlist) union(yl termlist) termlist {
return append(xl, yl...).norm()
}
// intersect returns the intersection xl ∩ yl.
func (xl termlist) intersect(yl termlist) termlist {
if xl.isEmpty() || yl.isEmpty() {
return nil
}
// Quadratic algorithm, but good enough for now.
// TODO(gri) fix asymptotic performance
var rl termlist
for _, x := range xl {
for _, y := range yl {
if r := x.intersect(y); r != nil {
rl = append(rl, r)
}
}
}
return rl.norm()
}
// equal reports whether xl and yl represent the same type set.
func (xl termlist) equal(yl termlist) bool {
// TODO(gri) this should be more efficient
return xl.subsetOf(yl) && yl.subsetOf(xl)
}
// includes reports whether t ∈ xl.
func (xl termlist) includes(t types.Type) bool {
for _, x := range xl {
if x.includes(t) {
return true
}
}
return false
}
// supersetOf reports whether y ⊆ xl.
func (xl termlist) supersetOf(y *term) bool {
for _, x := range xl {
if y.subsetOf(x) {
return true
}
}
return false
}
// subsetOf reports whether xl ⊆ yl.
func (xl termlist) subsetOf(yl termlist) bool {
if yl.isEmpty() {
return xl.isEmpty()
}
// each term x of xl must be a subset of yl
for _, x := range xl {
if !yl.supersetOf(x) {
return false // x is not a subset yl
}
}
return true
}

View File

@@ -0,0 +1,169 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by copytermlist.go DO NOT EDIT.
package typeparams
import "go/types"
// A term describes elementary type sets:
//
// ∅: (*term)(nil) == ∅ // set of no types (empty set)
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
// T: &term{false, T} == {T} // set of type T
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
type term struct {
tilde bool // valid if typ != nil
typ types.Type
}
func (x *term) String() string {
switch {
case x == nil:
return "∅"
case x.typ == nil:
return "𝓤"
case x.tilde:
return "~" + x.typ.String()
default:
return x.typ.String()
}
}
// equal reports whether x and y represent the same type set.
func (x *term) equal(y *term) bool {
// easy cases
switch {
case x == nil || y == nil:
return x == y
case x.typ == nil || y.typ == nil:
return x.typ == y.typ
}
// ∅ ⊂ x, y ⊂ 𝓤
return x.tilde == y.tilde && types.Identical(x.typ, y.typ)
}
// union returns the union x y: zero, one, or two non-nil terms.
func (x *term) union(y *term) (_, _ *term) {
// easy cases
switch {
case x == nil && y == nil:
return nil, nil // ∅ ∅ == ∅
case x == nil:
return y, nil // ∅ y == y
case y == nil:
return x, nil // x ∅ == x
case x.typ == nil:
return x, nil // 𝓤 y == 𝓤
case y.typ == nil:
return y, nil // x 𝓤 == 𝓤
}
// ∅ ⊂ x, y ⊂ 𝓤
if x.disjoint(y) {
return x, y // x y == (x, y) if x ∩ y == ∅
}
// x.typ == y.typ
// ~t ~t == ~t
// ~t T == ~t
// T ~t == ~t
// T T == T
if x.tilde || !y.tilde {
return x, nil
}
return y, nil
}
// intersect returns the intersection x ∩ y.
func (x *term) intersect(y *term) *term {
// easy cases
switch {
case x == nil || y == nil:
return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅
case x.typ == nil:
return y // 𝓤 ∩ y == y
case y.typ == nil:
return x // x ∩ 𝓤 == x
}
// ∅ ⊂ x, y ⊂ 𝓤
if x.disjoint(y) {
return nil // x ∩ y == ∅ if x ∩ y == ∅
}
// x.typ == y.typ
// ~t ∩ ~t == ~t
// ~t ∩ T == T
// T ∩ ~t == T
// T ∩ T == T
if !x.tilde || y.tilde {
return x
}
return y
}
// includes reports whether t ∈ x.
func (x *term) includes(t types.Type) bool {
// easy cases
switch {
case x == nil:
return false // t ∈ ∅ == false
case x.typ == nil:
return true // t ∈ 𝓤 == true
}
// ∅ ⊂ x ⊂ 𝓤
u := t
if x.tilde {
u = under(u)
}
return types.Identical(x.typ, u)
}
// subsetOf reports whether x ⊆ y.
func (x *term) subsetOf(y *term) bool {
// easy cases
switch {
case x == nil:
return true // ∅ ⊆ y == true
case y == nil:
return false // x ⊆ ∅ == false since x != ∅
case y.typ == nil:
return true // x ⊆ 𝓤 == true
case x.typ == nil:
return false // 𝓤 ⊆ y == false since y != 𝓤
}
// ∅ ⊂ x, y ⊂ 𝓤
if x.disjoint(y) {
return false // x ⊆ y == false if x ∩ y == ∅
}
// x.typ == y.typ
// ~t ⊆ ~t == true
// ~t ⊆ T == false
// T ⊆ ~t == true
// T ⊆ T == true
return !x.tilde || y.tilde
}
// disjoint reports whether x ∩ y == ∅.
// x.typ and y.typ must not be nil.
func (x *term) disjoint(y *term) bool {
if debug && (x.typ == nil || y.typ == nil) {
panic("invalid argument(s)")
}
ux := x.typ
if y.tilde {
ux = under(ux)
}
uy := y.typ
if x.tilde {
uy = under(uy)
}
return !types.Identical(ux, uy)
}

525
internal/typeutil/map.go Normal file
View File

@@ -0,0 +1,525 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package typeutil defines various utilities for types, such as Map,
// a mapping from types.Type to interface{} values.
package typeutil
import (
"bytes"
"fmt"
"go/types"
"reflect"
"github.com/goplus/llgo/internal/aliases"
"github.com/goplus/llgo/internal/typeparams"
)
// Map is a hash-table-based mapping from types (types.Type) to
// arbitrary interface{} values. The concrete types that implement
// the Type interface are pointers. Since they are not canonicalized,
// == cannot be used to check for equivalence, and thus we cannot
// simply use a Go map.
//
// Just as with map[K]V, a nil *Map is a valid empty map.
//
// Not thread-safe.
type Map struct {
hasher Hasher // shared by many Maps
table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
length int // number of map entries
}
// entry is an entry (key/value association) in a hash bucket.
type entry struct {
key types.Type
value interface{}
}
// SetHasher sets the hasher used by Map.
//
// All Hashers are functionally equivalent but contain internal state
// used to cache the results of hashing previously seen types.
//
// A single Hasher created by MakeHasher() may be shared among many
// Maps. This is recommended if the instances have many keys in
// common, as it will amortize the cost of hash computation.
//
// A Hasher may grow without bound as new types are seen. Even when a
// type is deleted from the map, the Hasher never shrinks, since other
// types in the map may reference the deleted type indirectly.
//
// Hashers are not thread-safe, and read-only operations such as
// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
// read-lock) is require around all Map operations if a shared
// hasher is accessed from multiple threads.
//
// If SetHasher is not called, the Map will create a private hasher at
// the first call to Insert.
func (m *Map) SetHasher(hasher Hasher) {
m.hasher = hasher
}
// Delete removes the entry with the given key, if any.
// It returns true if the entry was found.
func (m *Map) Delete(key types.Type) bool {
if m != nil && m.table != nil {
hash := m.hasher.Hash(key)
bucket := m.table[hash]
for i, e := range bucket {
if e.key != nil && types.Identical(key, e.key) {
// We can't compact the bucket as it
// would disturb iterators.
bucket[i] = entry{}
m.length--
return true
}
}
}
return false
}
// At returns the map entry for the given key.
// The result is nil if the entry is not present.
func (m *Map) At(key types.Type) interface{} {
if m != nil && m.table != nil {
for _, e := range m.table[m.hasher.Hash(key)] {
if e.key != nil && types.Identical(key, e.key) {
return e.value
}
}
}
return nil
}
// Set sets the map entry for key to val,
// and returns the previous entry, if any.
func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
if m.table != nil {
hash := m.hasher.Hash(key)
bucket := m.table[hash]
var hole *entry
for i, e := range bucket {
if e.key == nil {
hole = &bucket[i]
} else if types.Identical(key, e.key) {
prev = e.value
bucket[i].value = value
return
}
}
if hole != nil {
*hole = entry{key, value} // overwrite deleted entry
} else {
m.table[hash] = append(bucket, entry{key, value})
}
} else {
if m.hasher.memo == nil {
m.hasher = MakeHasher()
}
hash := m.hasher.Hash(key)
m.table = map[uint32][]entry{hash: {entry{key, value}}}
}
m.length++
return
}
// Len returns the number of map entries.
func (m *Map) Len() int {
if m != nil {
return m.length
}
return 0
}
// Iterate calls function f on each entry in the map in unspecified order.
//
// If f should mutate the map, Iterate provides the same guarantees as
// Go maps: if f deletes a map entry that Iterate has not yet reached,
// f will not be invoked for it, but if f inserts a map entry that
// Iterate has not yet reached, whether or not f will be invoked for
// it is unspecified.
func (m *Map) Iterate(f func(key types.Type, value interface{})) {
if m != nil {
for _, bucket := range m.table {
for _, e := range bucket {
if e.key != nil {
f(e.key, e.value)
}
}
}
}
}
// Keys returns a new slice containing the set of map keys.
// The order is unspecified.
func (m *Map) Keys() []types.Type {
keys := make([]types.Type, 0, m.Len())
m.Iterate(func(key types.Type, _ interface{}) {
keys = append(keys, key)
})
return keys
}
func (m *Map) toString(values bool) string {
if m == nil {
return "{}"
}
var buf bytes.Buffer
fmt.Fprint(&buf, "{")
sep := ""
m.Iterate(func(key types.Type, value interface{}) {
fmt.Fprint(&buf, sep)
sep = ", "
fmt.Fprint(&buf, key)
if values {
fmt.Fprintf(&buf, ": %q", value)
}
})
fmt.Fprint(&buf, "}")
return buf.String()
}
// String returns a string representation of the map's entries.
// Values are printed using fmt.Sprintf("%v", v).
// Order is unspecified.
func (m *Map) String() string {
return m.toString(true)
}
// KeysString returns a string representation of the map's key set.
// Order is unspecified.
func (m *Map) KeysString() string {
return m.toString(false)
}
////////////////////////////////////////////////////////////////////////
// Hasher
// A Hasher maps each type to its hash value.
// For efficiency, a hasher uses memoization; thus its memory
// footprint grows monotonically over time.
// Hashers are not thread-safe.
// Hashers have reference semantics.
// Call MakeHasher to create a Hasher.
type Hasher struct {
memo map[types.Type]uint32
// ptrMap records pointer identity.
ptrMap map[interface{}]uint32
// sigTParams holds type parameters from the signature being hashed.
// Signatures are considered identical modulo renaming of type parameters, so
// within the scope of a signature type the identity of the signature's type
// parameters is just their index.
//
// Since the language does not currently support referring to uninstantiated
// generic types or functions, and instantiated signatures do not have type
// parameter lists, we should never encounter a second non-empty type
// parameter list when hashing a generic signature.
sigTParams *types.TypeParamList
}
// MakeHasher returns a new Hasher instance.
func MakeHasher() Hasher {
return Hasher{
memo: make(map[types.Type]uint32),
ptrMap: make(map[interface{}]uint32),
sigTParams: nil,
}
}
// Hash computes a hash value for the given type t such that
// Identical(t, t') => Hash(t) == Hash(t').
func (h Hasher) Hash(t types.Type) uint32 {
hash, ok := h.memo[t]
if !ok {
hash = h.hashFor(t)
h.memo[t] = hash
}
return hash
}
// hashString computes the FowlerNollVo hash of s.
func hashString(s string) uint32 {
var h uint32
for i := 0; i < len(s); i++ {
h ^= uint32(s[i])
h *= 16777619
}
return h
}
func HashSig(h Hasher, t *types.Signature) uint32 {
var hash uint32 = 9091
if t.Variadic() {
hash *= 8863
}
// Use a separate hasher for types inside of the signature, where type
// parameter identity is modified to be (index, constraint). We must use a
// new memo for this hasher as type identity may be affected by this
// masking. For example, in func[T any](*T), the identity of *T depends on
// whether we are mapping the argument in isolation, or recursively as part
// of hashing the signature.
//
// We should never encounter a generic signature while hashing another
// generic signature, but defensively set sigTParams only if h.mask is
// unset.
tparams := t.TypeParams()
if h.sigTParams == nil && tparams.Len() != 0 {
h = Hasher{
// There may be something more efficient than discarding the existing
// memo, but it would require detecting whether types are 'tainted' by
// references to type parameters.
memo: make(map[types.Type]uint32),
// Re-using ptrMap ensures that pointer identity is preserved in this
// hasher.
ptrMap: h.ptrMap,
sigTParams: tparams,
}
}
for i := 0; i < tparams.Len(); i++ {
tparam := tparams.At(i)
hash += 7 * h.Hash(tparam.Constraint())
}
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
}
// hashFor computes the hash of t.
func (h Hasher) hashFor(t types.Type) uint32 {
// See Identical for rationale.
switch t := t.(type) {
case *types.Basic:
return uint32(t.Kind())
case *aliases.Alias:
return h.Hash(t.Underlying())
case *types.Array:
return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
case *types.Slice:
return 9049 + 2*h.Hash(t.Elem())
case *types.Struct:
var hash uint32 = 9059
for i, n := 0, t.NumFields(); i < n; i++ {
f := t.Field(i)
if f.Anonymous() {
hash += 8861
}
hash += hashString(t.Tag(i))
hash += hashString(f.Name()) // (ignore f.Pkg)
hash += h.Hash(f.Type())
}
return hash
case *types.Pointer:
return 9067 + 2*h.Hash(t.Elem())
case *types.Signature:
return HashSig(h, t)
case *types.Union:
return h.hashUnion(t)
case *types.Interface:
// Interfaces are identical if they have the same set of methods, with
// identical names and types, and they have the same set of type
// restrictions. See go/types.identical for more details.
var hash uint32 = 9103
// Hash methods.
for i, n := 0, t.NumMethods(); i < n; i++ {
// Method order is not significant.
// Ignore m.Pkg().
m := t.Method(i)
// Use shallow hash on method signature to
// avoid anonymous interface cycles.
hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type())
}
// Hash type restrictions.
terms, err := typeparams.InterfaceTermSet(t)
// if err != nil t has invalid type restrictions.
if err == nil {
hash += h.hashTermSet(terms)
}
return hash
case *types.Map:
return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
case *types.Chan:
return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
case *types.Named:
hash := h.hashPtr(t.Obj())
targs := t.TypeArgs()
for i := 0; i < targs.Len(); i++ {
targ := targs.At(i)
hash += 2 * h.Hash(targ)
}
return hash
case *types.TypeParam:
return h.hashTypeParam(t)
case *types.Tuple:
return h.hashTuple(t)
case interface{ Hash(h Hasher) uint32 }:
return t.Hash(h)
}
panic(fmt.Sprintf("%T: %v", t, t))
}
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
// See go/types.identicalTypes for rationale.
n := tuple.Len()
hash := 9137 + 2*uint32(n)
for i := 0; i < n; i++ {
hash += 3 * h.Hash(tuple.At(i).Type())
}
return hash
}
func (h Hasher) hashUnion(t *types.Union) uint32 {
// Hash type restrictions.
terms, err := typeparams.UnionTermSet(t)
// if err != nil t has invalid type restrictions. Fall back on a non-zero
// hash.
if err != nil {
return 9151
}
return h.hashTermSet(terms)
}
func (h Hasher) hashTermSet(terms []*types.Term) uint32 {
hash := 9157 + 2*uint32(len(terms))
for _, term := range terms {
// term order is not significant.
termHash := h.Hash(term.Type())
if term.Tilde() {
termHash *= 9161
}
hash += 3 * termHash
}
return hash
}
// hashTypeParam returns a hash of the type parameter t, with a hash value
// depending on whether t is contained in h.sigTParams.
//
// If h.sigTParams is set and contains t, then we are in the process of hashing
// a signature, and the hash value of t must depend only on t's index and
// constraint: signatures are considered identical modulo type parameter
// renaming. To avoid infinite recursion, we only hash the type parameter
// index, and rely on types.Identical to handle signatures where constraints
// are not identical.
//
// Otherwise the hash of t depends only on t's pointer identity.
func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 {
if h.sigTParams != nil {
i := t.Index()
if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) {
return 9173 + 3*uint32(i)
}
}
return h.hashPtr(t.Obj())
}
// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that
// pointers values are not dependent on the GC.
func (h Hasher) hashPtr(ptr interface{}) uint32 {
if hash, ok := h.ptrMap[ptr]; ok {
return hash
}
hash := uint32(reflect.ValueOf(ptr).Pointer())
h.ptrMap[ptr] = hash
return hash
}
// shallowHash computes a hash of t without looking at any of its
// element Types, to avoid potential anonymous cycles in the types of
// interface methods.
//
// When an unnamed non-empty interface type appears anywhere among the
// arguments or results of an interface method, there is a potential
// for endless recursion. Consider:
//
// type X interface { m() []*interface { X } }
//
// The problem is that the Methods of the interface in m's result type
// include m itself; there is no mention of the named type X that
// might help us break the cycle.
// (See comment in go/types.identical, case *Interface, for more.)
func (h Hasher) shallowHash(t types.Type) uint32 {
// t is the type of an interface method (Signature),
// its params or results (Tuples), or their immediate
// elements (mostly Slice, Pointer, Basic, Named),
// so there's no need to optimize anything else.
switch t := t.(type) {
case *aliases.Alias:
return h.shallowHash(t.Underlying())
case *types.Signature:
var hash uint32 = 604171
if t.Variadic() {
hash *= 971767
}
// The Signature/Tuple recursion is always finite
// and invariably shallow.
return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results())
case *types.Tuple:
n := t.Len()
hash := 9137 + 2*uint32(n)
for i := 0; i < n; i++ {
hash += 53471161 * h.shallowHash(t.At(i).Type())
}
return hash
case *types.Basic:
return 45212177 * uint32(t.Kind())
case *types.Array:
return 1524181 + 2*uint32(t.Len())
case *types.Slice:
return 2690201
case *types.Struct:
return 3326489
case *types.Pointer:
return 4393139
case *types.Union:
return 562448657
case *types.Interface:
return 2124679 // no recursion here
case *types.Map:
return 9109
case *types.Chan:
return 9127
case *types.Named:
return h.hashPtr(t.Obj())
case *types.TypeParam:
return h.hashPtr(t.Obj())
}
panic(fmt.Sprintf("shallowHash: %T: %v", t, t))
}

View File

@@ -17,13 +17,16 @@
package ssa_test
import (
"go/types"
"testing"
"github.com/goplus/llgo/cl/cltest"
"github.com/goplus/llgo/internal/typeutil"
"github.com/goplus/llgo/ssa"
)
func TestFromTestcgo(t *testing.T) {
cltest.FromDir(t, "", "../cl/_testcgo", true)
func TestFromTestrt(t *testing.T) {
cltest.FromDir(t, "", "../cl/_testrt", true)
}
func TestFromTestdata(t *testing.T) {
@@ -34,3 +37,17 @@ 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")
}
func TestMap(t *testing.T) {
var m typeutil.Map
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
m.Set(sig, 1)
csig := (*ssa.CFuncPtr)(sig)
m.Set(csig, 2)
if v := m.At(sig); v.(int) != 1 {
t.Fatal("At(sig):", v)
}
if v := m.At(csig); v.(int) != 2 {
t.Fatal("At(csig):", v)
}
}

View File

@@ -23,7 +23,6 @@ import (
"go/token"
"go/types"
"log"
"unsafe"
"github.com/goplus/llvm"
)
@@ -35,6 +34,13 @@ type Expr struct {
Type
}
var Nil Expr // Zero value is a nil Expr
// IsNil checks if the expression is nil or not.
func (v Expr) IsNil() bool {
return v.Type == nil
}
/*
// TypeOf returns the type of the expression.
func (v Expr) TypeOf() types.Type {
@@ -43,13 +49,19 @@ func (v Expr) TypeOf() types.Type {
*/
// Do evaluates the delay expression and returns the result.
func (v Expr) Do() Expr {
if vt := v.Type; vt.kind == vkDelayExpr {
func (v Expr) Do(b Builder) Expr {
switch vt := v.Type; vt.kind {
case vkDelayExpr:
return vt.t.(delayExprTy)()
case vkPhisExpr:
e := vt.t.(*phisExprTy)
return b.aggregateValue(e.Type, e.phis...)
}
return v
}
// -----------------------------------------------------------------------------
// DelayExpr returns a delay expression.
func DelayExpr(f func() Expr) Expr {
return Expr{Type: &aType{t: delayExprTy(f), kind: vkDelayExpr}}
@@ -67,12 +79,21 @@ func (p delayExprTy) String() string {
// -----------------------------------------------------------------------------
func llvmValues(vals []Expr) []llvm.Value {
ret := make([]llvm.Value, len(vals))
for i, v := range vals {
ret[i] = v.impl
}
return ret
type phisExprTy struct {
phis []llvm.Value
Type
}
func (p phisExprTy) Underlying() types.Type {
panic("don't call")
}
func (p phisExprTy) String() string {
return "phisExpr"
}
func phisExpr(t Type, phis []llvm.Value) Expr {
return Expr{Type: &aType{t: &phisExprTy{phis, t}, kind: vkPhisExpr}}
}
// -----------------------------------------------------------------------------
@@ -82,20 +103,6 @@ func (p Program) Null(t Type) Expr {
return Expr{llvm.ConstNull(t.ll), t}
}
// CStringVal returns a c-style string constant expression.
func (p Program) CStringVal(v string) Expr {
t := p.CString()
return Expr{llvm.ConstString(v, true), t}
}
// StringVal returns string constant expression.
func (p Program) StringVal(v string) Expr {
t := p.String()
cstr := llvm.ConstString(v, true)
// TODO(xsw): cstr => gostring
return Expr{cstr, t}
}
// BoolVal returns a boolean constant expression.
func (p Program) BoolVal(v bool) Expr {
t := p.Bool()
@@ -113,6 +120,11 @@ func (p Program) IntVal(v uint64, t Type) Expr {
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 {
switch v := v.(type) {
@@ -132,7 +144,7 @@ func (p Program) Val(v interface{}) Expr {
// Const returns a constant expression.
func (b Builder) Const(v constant.Value, typ Type) Expr {
prog := b.prog
prog := b.Prog
if v == nil {
return prog.Null(typ)
}
@@ -142,15 +154,44 @@ func (b Builder) Const(v constant.Value, typ Type) Expr {
switch {
case kind == types.Bool:
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 {
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 prog.StringVal(constant.StringVal(v))
return b.Str(constant.StringVal(v))
}
}
panic("todo")
panic(fmt.Sprintf("unsupported Const: %v, %v", v, typ.t))
}
// SizeOf returns the size of a type.
func (b Builder) SizeOf(t Type, n ...int64) Expr {
prog := b.Prog
size := prog.SizeOf(t, n...)
return prog.IntVal(size, prog.Uintptr())
}
// CStr returns a c-style string constant expression.
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
}
// -----------------------------------------------------------------------------
@@ -259,13 +300,18 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
case isMathOp(op): // op: + - * / %
kind := x.kind
switch kind {
case vkString, vkComplex:
panic("todo")
case vkString:
if op == token.ADD {
pkg := b.fn.pkg
return b.InlineCall(pkg.rtFunc("StringCat"), x, y)
}
case vkComplex:
default:
idx := mathOpIdx(op, kind)
if llop := mathOpToLLVM[idx]; llop != 0 {
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
}
}
case isLogicOp(op): // op: & | ^ << >> &^
if op == token.AND_NOT {
panic("todo")
@@ -277,7 +323,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
}
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
case isPredOp(op): // op: == != < <= < >=
tret := b.prog.Bool()
tret := b.Prog.Bool()
kind := x.kind
switch kind {
case vkSigned:
@@ -313,12 +359,116 @@ func (b Builder) UnOp(op token.Token, x Expr) Expr {
panic("todo")
}
// -----------------------------------------------------------------------------
func checkExpr(v Expr, t types.Type, b Builder) Expr {
if _, ok := t.(*types.Signature); ok {
if v.kind != vkClosure {
prog := b.Prog
nilVal := prog.Null(prog.VoidPtr()).impl
return b.aggregateValue(prog.Type(t), v.impl, nilVal)
}
}
return v
}
func llvmValues(vals []Expr, params *types.Tuple, b Builder) []llvm.Value {
n := params.Len()
ret := make([]llvm.Value, len(vals))
for i, v := range vals {
if i < n {
v = checkExpr(v, params.At(i).Type(), b)
}
ret[i] = v.impl
}
return ret
}
func llvmDelayValues(f func(i int) Expr, n int) []llvm.Value {
ret := make([]llvm.Value, n)
for i := 0; i < n; i++ {
ret[i] = f(i).impl
}
return ret
}
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(b Builder, bblks []BasicBlock, f func(i int) Expr) {
bs := llvmBlocks(bblks)
if p.kind != vkPhisExpr { // normal phi node
vs := llvmDelayValues(f, len(bblks))
p.impl.AddIncoming(vs, bs)
return
}
e := p.t.(*phisExprTy)
phis := e.phis
vals := make([][]llvm.Value, len(phis))
for iblk, blk := range bblks {
last := blk.impl.LastInstruction()
b.impl.SetInsertPointBefore(last)
impl := b.impl
val := f(iblk).impl
for i := range phis {
if iblk == 0 {
vals[i] = make([]llvm.Value, len(bblks))
}
vals[i][iblk] = llvm.CreateExtractValue(impl, val, i)
}
}
for i, phi := range phis {
phi.AddIncoming(vals[i], bs)
}
}
// Phi returns a phi node.
func (b Builder) Phi(t Type) Phi {
impl := b.impl
switch tund := t.t.Underlying().(type) {
case *types.Basic:
kind := tund.Kind()
switch kind {
case types.String:
prog := b.Prog
phis := make([]llvm.Value, 2)
phis[0] = llvm.CreatePHI(impl, prog.tyVoidPtr())
phis[1] = llvm.CreatePHI(impl, prog.tyInt())
return Phi{phisExpr(t, phis)}
}
}
phi := llvm.CreatePHI(impl, t.ll)
return Phi{Expr{phi, t}}
}
// -----------------------------------------------------------------------------
// Advance returns the pointer ptr advanced by offset bytes.
func (b Builder) Advance(ptr Expr, offset Expr) Expr {
if debugInstr {
log.Printf("Advance %v, %v\n", ptr.impl, offset.impl)
}
ret := llvm.CreateGEP(b.impl, b.Prog.tyInt8(), ptr.impl, []llvm.Value{offset.impl})
return Expr{ret, ptr.Type}
}
// Load returns the value at the pointer ptr.
func (b Builder) Load(ptr Expr) Expr {
if debugInstr {
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}
}
@@ -331,6 +481,20 @@ func (b Builder) Store(ptr, val Expr) Builder {
return b
}
// aggregateValue yields the value of the aggregate X with the fields
func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr {
if debugInstr {
log.Printf("AggregateValue %v, %v\n", t, flds)
}
impl := b.impl
tll := t.ll
ptr := llvm.CreateAlloca(impl, tll)
for i, fld := range flds {
impl.CreateStore(fld, llvm.CreateStructGEP(impl, tll, ptr, i))
}
return Expr{llvm.CreateLoad(b.impl, tll, ptr), t}
}
// The FieldAddr instruction yields the address of Field of *struct X.
//
// The field is identified by its index within the field list of the
@@ -341,12 +505,6 @@ func (b Builder) Store(ptr, val Expr) Builder {
//
// 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]
@@ -354,13 +512,22 @@ func (b Builder) FieldAddr(x Expr, idx int) Expr {
if debugInstr {
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
}
prog := b.prog
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 Field instruction yields the value of Field of struct X.
func (b Builder) Field(x Expr, idx int) Expr {
if debugInstr {
log.Printf("Field %v, %d\n", x.impl, idx)
}
telem := b.Prog.Field(x.Type, idx)
return Expr{llvm.CreateExtractValue(b.impl, x.impl, idx), telem}
}
// The IndexAddr instruction yields the address of the element at
// index `idx` of collection `x`. `idx` is an integer expression.
//
@@ -377,13 +544,177 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
if debugInstr {
log.Printf("IndexAddr %v, %v\n", x.impl, idx.impl)
}
prog := b.prog
prog := b.Prog
telem := prog.Index(x.Type)
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}
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, x.impl, indices), pt}
}
// The Index instruction yields element Index of collection X, an array,
// string or type parameter containing an array, a string, a pointer to an,
// array or a slice.
//
// Example printed form:
//
// t2 = t0[t1]
func (b Builder) Index(x, idx Expr, addr func(Expr) Expr) Expr {
if debugInstr {
log.Printf("Index %v, %v\n", x.impl, idx.impl)
}
prog := b.Prog
var telem Type
var ptr Expr
switch t := x.t.Underlying().(type) {
case *types.Basic:
if t.Info()&types.IsString == 0 {
panic(fmt.Errorf("invalid operation: cannot index %v", t))
}
telem = prog.Type(types.Typ[types.Byte])
pkg := b.fn.pkg
ptr = b.InlineCall(pkg.rtFunc("StringData"), x)
case *types.Array:
telem = prog.Index(x.Type)
if addr != nil {
ptr = addr(x)
} else {
size := b.SizeOf(telem, t.Len())
ptr = b.Alloca(size)
b.Store(ptr, x)
}
}
pt := prog.Pointer(telem)
indices := []llvm.Value{idx.impl}
buf := Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt}
return b.Load(buf)
}
// The 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.
//
// 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
var nCap Expr
var nEltSize Expr
var base Expr
if low.IsNil() {
low = prog.IntVal(0, prog.Int())
}
switch t := x.t.Underlying().(type) {
case *types.Basic:
if t.Kind() != types.String {
panic(fmt.Errorf("invalid operation: cannot slice %v", t))
}
if high.IsNil() {
high = b.InlineCall(pkg.rtFunc("StringLen"), x)
}
ret.Type = x.Type
ret.impl = b.InlineCall(pkg.rtFunc("NewStringSlice"), x, low, high).impl
return
case *types.Slice:
nEltSize = b.SizeOf(prog.Index(x.Type))
nCap = b.InlineCall(pkg.rtFunc("SliceCap"), x)
if high.IsNil() {
high = b.InlineCall(pkg.rtFunc("SliceLen"), x)
}
ret.Type = x.Type
base = b.InlineCall(pkg.rtFunc("SliceData"), x)
case *types.Pointer:
telem := t.Elem()
switch te := telem.Underlying().(type) {
case *types.Array:
elem := prog.Type(te.Elem())
ret.Type = prog.Slice(elem)
nEltSize = b.SizeOf(elem)
nCap = prog.IntVal(uint64(te.Len()), prog.Int())
if high.IsNil() {
high = nCap
}
base = x
}
}
if max.IsNil() {
max = nCap
}
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice3"), base, nEltSize, nCap, low, high, max).impl
return
}
// -----------------------------------------------------------------------------
// 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.
//
// 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 MakeSlice instruction yields a slice of length Len backed by a
// newly allocated array of length Cap.
//
// Both Len and Cap must be non-nil Values of integer type.
//
// (Alloc(types.Array) followed by Slice will not suffice because
// Alloc can only create arrays of constant length.)
//
// Type() returns a (possibly named) *types.Slice.
//
// Example printed form:
//
// t1 = make []string 1:int t0
// t1 = make StringSlice 1:int t0
func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) {
if debugInstr {
log.Printf("MakeSlice %v, %v, %v\n", t, len.impl, cap.impl)
}
pkg := b.fn.pkg
if cap.IsNil() {
cap = len
}
elemSize := b.SizeOf(b.Prog.Index(t))
size := b.BinOp(token.MUL, cap, elemSize)
ptr := b.InlineCall(pkg.rtFunc("AllocZ"), size)
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice"), ptr, len, cap).impl
ret.Type = t
return
}
// -----------------------------------------------------------------------------
// The Alloc instruction reserves space for a variable of the given type,
// zero-initializes it, and yields its address.
//
@@ -407,20 +738,58 @@ func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) {
if debugInstr {
log.Printf("Alloc %v, %v\n", t, heap)
}
prog := b.prog
telem := t.Elem()
if heap {
prog := b.Prog
pkg := b.fn.pkg
size := unsafe.Sizeof(telem)
ret = b.Call(pkg.rtFunc("Alloc"), prog.Val(size))
elem := prog.Type(t.Elem())
size := b.SizeOf(elem)
if heap {
ret = b.InlineCall(pkg.rtFunc("AllocZ"), size)
} else {
ret.impl = llvm.CreateAlloca(b.impl, prog.Type(telem).ll)
ret = Expr{llvm.CreateAlloca(b.impl, elem.ll), prog.VoidPtr()}
ret.impl = b.InlineCall(pkg.rtFunc("Zeroinit"), ret, size).impl
}
// TODO(xsw): zero-initialize
ret.Type = prog.Type(t)
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().
//
@@ -452,7 +821,7 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
switch typ.(type) {
default:
ret.impl = b.impl.CreateBitCast(x.impl, t.ll, "bitCast")
ret.Type = b.prog.Type(typ)
ret.Type = b.Prog.Type(typ)
return
}
}
@@ -480,15 +849,12 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) {
// 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)
ret.Type = b.Prog.Type(typ)
switch und := typ.Underlying().(type) {
case *types.Basic:
kind := und.Kind()
@@ -535,9 +901,6 @@ func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value {
//
// 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)
@@ -546,17 +909,24 @@ func (b Builder) MakeInterface(inter types.Type, x Expr, mayDelay bool) (ret Exp
if debugInstr {
log.Printf("MakeInterface %v, %v\n", inter, x.impl)
}
t := inter.Underlying().(*types.Interface)
isAny := t.Empty()
tiund := inter.Underlying().(*types.Interface)
isAny := tiund.Empty()
fnDo := func() Expr {
prog := b.Prog
pkg := b.fn.pkg
switch x.kind {
case vkSigned, vkUnsigned, vkFloat:
fn := pkg.rtFunc("MakeAnyInt")
return b.InlineCall(fn, x)
case vkString:
fn := pkg.rtFunc("MakeAnyString")
return b.InlineCall(fn, x)
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")
}
@@ -624,7 +994,7 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
default:
panic("todo")
}
typ := b.InlineCall(pkg.rtFunc("Basic"), b.prog.Val(int(kind)))
typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind)))
return b.InlineCall(fn, x, typ)
}
panic("todo")
@@ -649,21 +1019,41 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
// t4 = t3()
// t7 = invoke t5.Println(...t6)
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
prog := b.Prog
if debugInstr {
var b bytes.Buffer
fmt.Fprint(&b, "Call ", fn.impl.Name())
name := fn.impl.Name()
if name == "" {
name = "closure"
}
fmt.Fprint(&b, "Call ", fn.t, " ", name)
sep := ": "
for _, arg := range args {
fmt.Fprint(&b, ", ", arg.impl)
fmt.Fprint(&b, sep, arg.impl)
sep = ", "
}
log.Println(b.String())
}
switch t := fn.t.(type) {
case *types.Signature:
ret.Type = b.prog.retType(t)
var sig *types.Signature
var t = fn.t
normal := true
switch fn.kind {
case vkClosure:
fn = b.Field(fn, 0)
t = fn.t
normal = false
fallthrough
case vkFuncDecl, vkFuncPtr:
sig = t.(*types.Signature)
ret.Type = prog.retType(sig)
default:
panic("todo")
panic("unreachable")
}
if normal {
ret.impl = llvm.CreateCall(b.impl, fn.ll, fn.impl, llvmValues(args, sig.Params(), b))
} else {
ret = prog.IntVal(0, prog.Type(types.Typ[types.Int32]))
}
ret.impl = llvm.CreateCall(b.impl, fn.ll, fn.impl, llvmValues(args))
return
}
@@ -674,6 +1064,28 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
// `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 t := arg.t.Underlying().(type) {
case *types.Slice:
return b.InlineCall(b.fn.pkg.rtFunc("SliceLen"), arg)
case *types.Basic:
if t.Kind() == types.String {
return b.InlineCall(b.fn.pkg.rtFunc("StringLen"), arg)
}
}
}
case "cap":
if len(args) == 1 {
arg := args[0]
switch arg.t.Underlying().(type) {
case *types.Slice:
return b.InlineCall(b.fn.pkg.rtFunc("SliceCap"), arg)
}
}
}
panic("todo")
}

View File

@@ -17,11 +17,10 @@
package ssa
import (
"go/constant"
"go/types"
"github.com/goplus/llgo/internal/typeutil"
"github.com/goplus/llvm"
"golang.org/x/tools/go/types/typeutil"
)
const (
@@ -97,6 +96,7 @@ func Initialize(flags InitFlags) {
type aProgram struct {
ctx llvm.Context
typs typeutil.Map
// sizs types.Sizes
rt *types.Package
rtget func() *types.Package
@@ -117,9 +117,11 @@ type aProgram struct {
rtStringTy llvm.Type
rtIfaceTy llvm.Type
rtSliceTy llvm.Type
rtMapTy llvm.Type
anyTy Type
voidTy Type
voidPtr Type
boolTy Type
cstrTy Type
stringTy Type
@@ -139,9 +141,17 @@ func NewProgram(target *Target) Program {
target = &Target{}
}
ctx := llvm.NewContext()
// TODO(xsw): Finalize may cause panic, so comment it.
// ctx.Finalize()
td := llvm.NewTargetData("") // TODO(xsw): target config
/*
arch := target.GOARCH
if arch == "" {
arch = runtime.GOARCH
}
sizes := types.SizesFor("gc", arch)
// TODO(xsw): Finalize may cause panic, so comment it.
ctx.Finalize()
*/
return &aProgram{ctx: ctx, target: target, td: td}
}
@@ -184,6 +194,13 @@ func (p Program) rtIface() llvm.Type {
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
@@ -217,6 +234,13 @@ func (p Program) Void() Type {
return p.voidTy
}
func (p Program) VoidPtr() Type {
if p.voidPtr == nil {
p.voidPtr = p.Type(types.Typ[types.UnsafePointer])
}
return p.voidPtr
}
// Bool returns bool type.
func (p Program) Bool() Type {
if p.boolTy == nil {
@@ -225,7 +249,7 @@ func (p Program) Bool() Type {
return p.boolTy
}
func (p Program) CString() Type {
func (p Program) CStr() Type {
if p.cstrTy == nil { // *int8
p.cstrTy = p.Type(types.NewPointer(types.Typ[types.Int8]))
}
@@ -290,10 +314,12 @@ type aPackage struct {
type Package = *aPackage
/*
// NewConst creates a new named constant.
func (p Package) NewConst(name string, val constant.Value) NamedConst {
return &aNamedConst{}
}
*/
// NewVar creates a new global variable.
func (p Package) NewVar(name string, typ types.Type) Global {
@@ -314,7 +340,7 @@ func (p Package) NewFunc(name string, sig *types.Signature) Function {
if v, ok := p.fns[name]; ok {
return v
}
t := p.prog.llvmSignature(sig, false)
t := p.prog.llvmFuncDecl(sig)
fn := llvm.AddFunction(p.mod, name, t.ll)
ret := newFunction(fn, t, p, p.prog)
p.fns[name] = ret

View File

@@ -21,6 +21,8 @@ import (
"go/token"
"go/types"
"testing"
"github.com/goplus/llvm"
)
/*
@@ -30,15 +32,67 @@ func TestMakeInterface(t *testing.T) {
}
*/
func TestDelayExpr(t *testing.T) {
a := delayExprTy(nil)
_ = a.String()
func TestTypes(t *testing.T) {
ctx := llvm.NewContext()
llvmIntType(ctx, 4)
intT := types.NewVar(0, nil, "", types.Typ[types.Int])
ret := types.NewTuple(intT, intT)
sig := types.NewSignatureType(nil, nil, nil, nil, ret, false)
prog := NewProgram(nil)
prog.retType(sig)
}
func TestIndexType(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Log("TestDelayExpr: no error?")
t.Log("indexType: no error?")
}
}()
indexType(types.Typ[types.Int])
}
func TestCvtCType(t *testing.T) {
test := func(typ types.Type) {
defer func() {
if r := recover(); r == nil {
t.Log("cvtCType: no error?")
}
}()
cvtCType(typ)
}
test(types.NewInterfaceType(nil, nil))
a := types.NewTypeName(0, nil, "a", nil)
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
named := types.NewNamed(a, sig, nil)
test(named)
}
func TestCFuncPtr(t *testing.T) {
sig := types.NewSignatureType(nil, nil, nil, nil, nil, false)
csig := (*CFuncPtr)(sig)
_ = csig.String()
if csig.Underlying() != sig {
t.Fatal("TestCFuncPtr failed")
}
}
func TestUserdefExpr(t *testing.T) {
a := delayExprTy(nil)
b := &phisExprTy{}
_ = a.String()
_ = b.String()
test := func(a types.Type) {
defer func() {
if r := recover(); r == nil {
t.Log("TestUserdefExpr: no error?")
}
}()
a.Underlying()
}
test(a)
test(b)
}
func TestAny(t *testing.T) {

View File

@@ -19,6 +19,7 @@ package ssa
import (
"bytes"
"fmt"
"go/types"
"log"
"github.com/goplus/llvm"
@@ -50,7 +51,7 @@ func (p BasicBlock) Index() int {
type aBuilder struct {
impl llvm.Builder
fn Function
prog Program
Prog Program
}
// Builder represents a builder for creating instructions in a function.
@@ -73,7 +74,14 @@ func (b Builder) Panic(v Expr) {
if debugInstr {
log.Printf("Panic %v\n", v.impl)
}
b.impl.CreateUnreachable() // TODO(xsw): pass v
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.
@@ -95,7 +103,8 @@ func (b Builder) Return(results ...Expr) {
case 1:
b.impl.CreateRet(results[0].impl)
default:
b.impl.CreateAggregateRet(llvmValues(results))
tret := b.fn.t.(*types.Signature).Results()
b.impl.CreateAggregateRet(llvmValues(results, tret, b))
}
}
@@ -121,4 +130,21 @@ func (b Builder) If(cond Expr, thenb, elseb BasicBlock) {
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

@@ -18,6 +18,7 @@ package ssa
import (
"fmt"
"go/token"
"go/types"
"github.com/goplus/llvm"
@@ -40,29 +41,16 @@ const (
vkString
vkBool
vkPtr
vkFunc
vkFuncDecl // func decl
vkFuncPtr // func ptr in C
vkClosure // func ptr in Go
vkTuple
vkDelayExpr = -1
vkPhisExpr = -2
)
// -----------------------------------------------------------------------------
const (
NameValist = "__llgo_va_list"
)
func VArg() *types.Var {
return types.NewParam(0, nil, NameValist, types.Typ[types.Invalid])
}
func IsVArg(arg *types.Var) bool {
return arg.Name() == NameValist
}
func HasVArg(t *types.Tuple, n int) bool {
return n > 0 && IsVArg(t.At(n-1))
}
func indexType(t types.Type) types.Type {
switch t := t.(type) {
case *types.Slice:
@@ -99,11 +87,25 @@ func methodToFunc(sig *types.Signature) *types.Signature {
type aType struct {
ll llvm.Type
t types.Type
kind valueKind
kind valueKind // value kind of llvm.Type
}
type Type = *aType
// TODO(xsw):
// how to generate platform independent code?
func (p Program) SizeOf(typ Type, n ...int64) uint64 {
size := p.td.TypeAllocSize(typ.ll)
if len(n) != 0 {
size *= uint64(n[0])
}
return size
}
func (p Program) Slice(typ Type) Type {
return p.Type(types.NewSlice(typ.t))
}
func (p Program) Pointer(typ Type) Type {
return p.Type(types.NewPointer(typ.t))
}
@@ -119,13 +121,11 @@ func (p Program) Index(typ Type) Type {
func (p Program) Field(typ Type, i int) Type {
tunder := typ.t.Underlying()
return p.Type(tunder.(*types.Struct).Field(i).Type())
tfld := tunder.(*types.Struct).Field(i).Type()
return p.Type(tfld)
}
func (p Program) Type(typ types.Type) Type {
if sig, ok := typ.(*types.Signature); ok { // should methodToFunc
return p.llvmSignature(sig, true)
}
if v := p.typs.At(typ); v != nil {
return v.(Type)
}
@@ -134,14 +134,9 @@ func (p Program) Type(typ types.Type) Type {
return ret
}
func (p Program) llvmSignature(sig *types.Signature, isPtr bool) Type {
func (p Program) llvmFuncDecl(sig *types.Signature) Type {
sig = methodToFunc(sig)
if v := p.typs.At(sig); v != nil {
return v.(Type)
}
ret := p.toLLVMFunc(sig, isPtr)
p.typs.Set(sig, ret)
return ret
return p.toLLVMFunc(sig, false, true) // don't save func decl to cache
}
func (p Program) tyVoidPtr() llvm.Type {
@@ -252,10 +247,15 @@ func (p Program) toLLVMType(typ types.Type) Type {
case *types.Slice:
return &aType{p.rtSlice(), typ, vkInvalid}
case *types.Map:
return &aType{p.rtMap(), typ, vkInvalid}
case *types.Struct:
return p.toLLVMStruct(t)
case *types.Named:
return p.toLLVMNamed(t)
case *types.Signature:
return p.toLLVMFunc(t, false, false)
case *CFuncPtr:
return p.toLLVMFunc((*types.Signature)(t), true, false)
case *types.Array:
elem := p.Type(t.Elem())
return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid}
@@ -301,7 +301,8 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) {
return
}
func (p Program) toLLVMFunc(sig *types.Signature, isPtr bool) Type {
func (p Program) toLLVMFunc(sig *types.Signature, inC, isDecl bool) Type {
if isDecl || inC {
tParams := sig.Params()
n := tParams.Len()
hasVArg := HasVArg(tParams, n)
@@ -311,6 +312,7 @@ func (p Program) toLLVMFunc(sig *types.Signature, isPtr bool) Type {
params := p.toLLVMTypes(tParams, n)
out := sig.Results()
var ret llvm.Type
var kind valueKind
switch nret := out.Len(); nret {
case 0:
ret = p.tyVoid()
@@ -320,10 +322,21 @@ func (p Program) toLLVMFunc(sig *types.Signature, isPtr bool) Type {
ret = p.toLLVMTuple(out)
}
ft := llvm.FunctionType(ret, params, hasVArg)
if isPtr {
if isDecl {
kind = vkFuncDecl
} else {
ft = llvm.PointerType(ft, 0)
kind = vkFuncPtr
}
return &aType{ft, sig, vkFunc}
return &aType{ft, sig, kind}
}
flds := []*types.Var{
types.NewField(token.NoPos, nil, "f", (*CFuncPtr)(sig), false),
types.NewField(token.NoPos, nil, "data", types.Typ[types.UnsafePointer], false),
}
t := types.NewStruct(flds, nil)
ll := p.ctx.StructType(p.toLLVMFields(t), false)
return &aType{ll, t, vkClosure}
}
func (p Program) retType(sig *types.Signature) Type {

140
ssa/type_c.go Normal file
View File

@@ -0,0 +1,140 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ssa
import (
"go/types"
"github.com/goplus/llgo/internal/typeutil"
)
// -----------------------------------------------------------------------------
const (
NameValist = "__llgo_va_list"
)
func VArg() *types.Var {
return types.NewParam(0, nil, NameValist, types.Typ[types.Invalid])
}
func IsVArg(arg *types.Var) bool {
return arg.Name() == NameValist
}
func HasVArg(t *types.Tuple, n int) bool {
return n > 0 && IsVArg(t.At(n-1))
}
// -----------------------------------------------------------------------------
// CFuncPtr represents a C function pointer.
type CFuncPtr types.Signature
func (t *CFuncPtr) String() string { return (*types.Signature)(t).String() }
func (t *CFuncPtr) Underlying() types.Type { return (*types.Signature)(t) }
func (t *CFuncPtr) Hash(h typeutil.Hasher) uint32 {
return typeutil.HashSig(h, (*types.Signature)(t))*13 + 97
}
// -----------------------------------------------------------------------------
// CType convert a C type into Go.
func CType(typ types.Type) types.Type {
t, _ := cvtCType(typ)
return t
}
// CFuncDecl convert a C function decl into Go signature.
func CFuncDecl(sig *types.Signature) *types.Signature {
hasVArg := sig.Variadic()
params, cvt1 := cvtTuple(sig.Params(), hasVArg)
results, cvt2 := cvtTuple(sig.Results(), false)
if cvt1 || cvt2 {
return types.NewSignatureType(nil, nil, nil, params, results, hasVArg)
}
return sig
}
func cvtCType(typ types.Type) (types.Type, bool) {
switch t := typ.(type) {
case *types.Basic:
case *types.Pointer:
if elem, cvt := cvtCType(t.Elem()); cvt {
return types.NewPointer(elem), true
}
case *types.Struct:
return cvtCStruct(t)
case *types.Named:
if _, cvt := cvtCType(t.Underlying()); cvt {
panic("don't define named type")
}
case *types.Signature:
t = CFuncDecl(t)
return (*CFuncPtr)(t), true
case *types.Array:
if elem, cvt := cvtCType(t.Elem()); cvt {
return types.NewArray(elem, t.Len()), true
}
default:
panic("unreachable")
}
return typ, false
}
func cvtTuple(t *types.Tuple, hasVArg bool) (*types.Tuple, bool) {
n := t.Len()
vars := make([]*types.Var, n)
needcvt := false
if hasVArg {
n--
vars[n] = t.At(n)
}
for i := 0; i < n; i++ {
v := t.At(i)
if t, cvt := cvtCType(v.Type()); cvt {
v = types.NewParam(v.Pos(), v.Pkg(), v.Name(), t)
needcvt = true
}
vars[i] = v
}
if needcvt {
return types.NewTuple(vars...), true
}
return t, false
}
func cvtCStruct(typ *types.Struct) (*types.Struct, bool) {
n := typ.NumFields()
flds := make([]*types.Var, n)
needcvt := false
for i := 0; i < n; i++ {
f := typ.Field(i)
if t, cvt := cvtCType(f.Type()); cvt {
f = types.NewField(f.Pos(), f.Pkg(), f.Name(), t, f.Anonymous())
needcvt = true
}
flds[i] = f
}
if needcvt {
return types.NewStruct(flds, nil), true
}
return typ, false
}
// -----------------------------------------------------------------------------