Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92f56a2f90 | ||
|
|
926e2d4a2e | ||
|
|
e9153defee | ||
|
|
889fc8b6a9 | ||
|
|
9b9da3133d | ||
|
|
1c8edb0387 | ||
|
|
c0ef1598c9 | ||
|
|
bc1acee6f5 | ||
|
|
9f25d73826 | ||
|
|
f07a62d136 | ||
|
|
06d6b447e4 | ||
|
|
1cff02e4cc | ||
|
|
809a400f57 | ||
|
|
f1bb42f554 | ||
|
|
4fd8f84536 | ||
|
|
223c24450e | ||
|
|
8a7ddf4dc2 | ||
|
|
08217e5a5a | ||
|
|
424dbd9261 | ||
|
|
b615ada2c3 | ||
|
|
20a47873d0 | ||
|
|
d87ce1a124 | ||
|
|
91d012d33d | ||
|
|
330cb22351 | ||
|
|
236debab33 | ||
|
|
13a1c8ac4b | ||
|
|
29fad7b397 | ||
|
|
8eeac8a26d | ||
|
|
133d41d748 | ||
|
|
d444123062 | ||
|
|
4a5c8d3fbb | ||
|
|
afd3d40348 | ||
|
|
85da86a4f1 | ||
|
|
72d4f0f7f8 | ||
|
|
192b479f18 | ||
|
|
3e6dfa3c05 | ||
|
|
5bd28a1e9e | ||
|
|
a23a2601e4 | ||
|
|
3220b629c7 | ||
|
|
9cf122c31a | ||
|
|
75d513a78a | ||
|
|
3cbe4aac87 | ||
|
|
3e47a977e4 | ||
|
|
40855c2d2a | ||
|
|
b2319eda66 | ||
|
|
5c5b8e62e5 | ||
|
|
fbb1f89ab3 | ||
|
|
25b104cf13 | ||
|
|
be1599b418 | ||
|
|
d462e548b1 | ||
|
|
df1e4708f5 | ||
|
|
cf02f4a34f | ||
|
|
480cf09177 | ||
|
|
52a64a7770 | ||
|
|
8d3cb246c2 | ||
|
|
8f15fd45f2 | ||
|
|
afd02b3d78 |
@@ -2,7 +2,6 @@
|
||||
source_filename = "main"
|
||||
|
||||
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
||||
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@0 = private unnamed_addr constant [13 x i8] c"Hello world\0A\00", align 1
|
||||
@@ -43,7 +42,7 @@ declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/ll
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
|
||||
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
|
||||
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String")
|
||||
|
||||
|
||||
@@ -1,11 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/internal/runtime/c"
|
||||
)
|
||||
|
||||
var a int64 = 1<<63 - 1
|
||||
var b int64 = -1 << 63
|
||||
var c uint64 = 1<<64 - 1
|
||||
var n uint64 = 1<<64 - 1
|
||||
|
||||
func main() {
|
||||
var a = []int{1, 2, 3, 4}
|
||||
_ = len(a)
|
||||
_ = len([]int{1, 2, 3, 4})
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
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.c = 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:
|
||||
@@ -17,7 +23,7 @@ _llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
store i64 9223372036854775807, ptr @main.a, align 4
|
||||
store i64 -9223372036854775808, ptr @main.b, align 4
|
||||
store i64 -1, ptr @main.c, align 4
|
||||
store i64 -1, ptr @main.n, align 4
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
@@ -28,7 +34,7 @@ define void @main() {
|
||||
_llgo_0:
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
||||
%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
|
||||
@@ -37,26 +43,132 @@ _llgo_0:
|
||||
store i64 3, ptr %3, align 4
|
||||
%4 = getelementptr inbounds i64, ptr %0, i64 3
|
||||
store i64 4, ptr %4, align 4
|
||||
%5 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 4, i64 4)
|
||||
%6 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %5)
|
||||
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
||||
%8 = getelementptr inbounds i64, ptr %7, i64 0
|
||||
store i64 1, ptr %8, align 4
|
||||
%9 = getelementptr inbounds i64, ptr %7, i64 1
|
||||
store i64 2, ptr %9, align 4
|
||||
%10 = getelementptr inbounds i64, ptr %7, i64 2
|
||||
store i64 3, ptr %10, align 4
|
||||
%11 = getelementptr inbounds i64, ptr %7, i64 3
|
||||
store i64 4, ptr %11, align 4
|
||||
%12 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %7, i64 4, i64 4)
|
||||
%13 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %12)
|
||||
%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.Alloc"(i64)
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||
|
||||
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr, i64, i64)
|
||||
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
|
||||
|
||||
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
|
||||
|
||||
declare i64 @"github.com/goplus/llgo/internal/runtime.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
17
cl/_testrt/concat/in.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/internal/runtime/c"
|
||||
)
|
||||
|
||||
func concat(args ...string) (ret string) {
|
||||
for _, v := range args {
|
||||
ret += v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
result := concat("Hello", " ", "World")
|
||||
c.Fprintf(c.Stderr, c.Str("Hi, %s\n"), c.AllocaCStr(result))
|
||||
}
|
||||
106
cl/_testrt/concat/out.ll
Normal file
106
cl/_testrt/concat/out.ll
Normal file
@@ -0,0 +1,106 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
||||
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@0 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||
@1 = private unnamed_addr constant [6 x i8] c"Hello\00", align 1
|
||||
@2 = private unnamed_addr constant [2 x i8] c" \00", align 1
|
||||
@3 = private unnamed_addr constant [6 x i8] c"World\00", align 1
|
||||
@__stderrp = external global ptr
|
||||
@4 = private unnamed_addr constant [8 x i8] c"Hi, %s\0A\00", align 1
|
||||
|
||||
define %"github.com/goplus/llgo/internal/runtime.String" @main.concat(%"github.com/goplus/llgo/internal/runtime.Slice" %0) {
|
||||
_llgo_0:
|
||||
%1 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
|
||||
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 0)
|
||||
%3 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %2, 0
|
||||
%4 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %2, 1
|
||||
br label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_2, %_llgo_0
|
||||
%5 = phi ptr [ %3, %_llgo_0 ], [ %18, %_llgo_2 ]
|
||||
%6 = phi i64 [ %4, %_llgo_0 ], [ %19, %_llgo_2 ]
|
||||
%7 = phi i64 [ -1, %_llgo_0 ], [ %12, %_llgo_2 ]
|
||||
%8 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||
%9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %8, i32 0, i32 0
|
||||
store ptr %5, ptr %9, align 8
|
||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %8, i32 0, i32 1
|
||||
store i64 %6, ptr %10, align 4
|
||||
%11 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %8, align 8
|
||||
%12 = add i64 %7, 1
|
||||
%13 = icmp slt i64 %12, %1
|
||||
br i1 %13, label %_llgo_2, label %_llgo_3
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1
|
||||
%14 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0)
|
||||
%15 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %14, i64 %12
|
||||
%16 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %15, align 8
|
||||
%17 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String" %11, %"github.com/goplus/llgo/internal/runtime.String" %16)
|
||||
%18 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %17, 0
|
||||
%19 = extractvalue %"github.com/goplus/llgo/internal/runtime.String" %17, 1
|
||||
br label %_llgo_1
|
||||
|
||||
_llgo_3: ; preds = %_llgo_1
|
||||
ret %"github.com/goplus/llgo/internal/runtime.String" %11
|
||||
}
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 48)
|
||||
%1 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i64 0
|
||||
%2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 5)
|
||||
store %"github.com/goplus/llgo/internal/runtime.String" %2, ptr %1, align 8
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i64 1
|
||||
%4 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @2, i64 1)
|
||||
store %"github.com/goplus/llgo/internal/runtime.String" %4, ptr %3, align 8
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i64 2
|
||||
%6 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @3, i64 5)
|
||||
store %"github.com/goplus/llgo/internal/runtime.String" %6, ptr %5, align 8
|
||||
%7 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %0, i64 16, i64 3, i64 0, i64 3, i64 3)
|
||||
%8 = call %"github.com/goplus/llgo/internal/runtime.String" @main.concat(%"github.com/goplus/llgo/internal/runtime.Slice" %7)
|
||||
%9 = load ptr, ptr @__stderrp, align 8
|
||||
%10 = call i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String" %8)
|
||||
%11 = add i64 %10, 1
|
||||
%12 = alloca i8, i64 %11, align 1
|
||||
%13 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %12, %"github.com/goplus/llgo/internal/runtime.String" %8)
|
||||
%14 = call i32 (ptr, ptr, ...) @fprintf(ptr %9, ptr @4, ptr %13)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice")
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice")
|
||||
|
||||
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String", %"github.com/goplus/llgo/internal/runtime.String")
|
||||
|
||||
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||
|
||||
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
|
||||
|
||||
declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.String")
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String")
|
||||
|
||||
declare i32 @fprintf(ptr, ptr, ...)
|
||||
19
cl/_testrt/cvar/in.go
Normal file
19
cl/_testrt/cvar/in.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname barX _bar_x
|
||||
var barX struct {
|
||||
Arr [16]int8
|
||||
Callbacks [2]func()
|
||||
}
|
||||
|
||||
//go:linkname barY _bar_y
|
||||
var barY struct {
|
||||
Arr [16]int8
|
||||
}
|
||||
|
||||
func main() {
|
||||
_ = barX
|
||||
_ = barY
|
||||
}
|
||||
30
cl/_testrt/cvar/out.ll
Normal file
30
cl/_testrt/cvar/out.ll
Normal file
@@ -0,0 +1,30 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@_bar_x = external global ptr
|
||||
@_bar_y = external global ptr
|
||||
@"main.init$guard" = global ptr null
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%0 = load { [16 x i8], [2 x ptr] }, ptr @_bar_x, align 8
|
||||
%1 = load { [16 x i8] }, ptr @_bar_y, align 1
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
16
cl/_testrt/fprintf/in.go
Normal file
16
cl/_testrt/fprintf/in.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:linkname cstr llgo.cstr
|
||||
func cstr(string) *int8
|
||||
|
||||
//go:linkname stderr __stderrp
|
||||
var stderr unsafe.Pointer
|
||||
|
||||
//go:linkname fprintf C.fprintf
|
||||
func fprintf(fp unsafe.Pointer, format *int8, __llgo_va_list ...any)
|
||||
|
||||
func main() {
|
||||
fprintf(stderr, cstr("Hello %d\n"), 100)
|
||||
}
|
||||
32
cl/_testrt/fprintf/out.ll
Normal file
32
cl/_testrt/fprintf/out.ll
Normal file
@@ -0,0 +1,32 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@__stderrp = external global ptr
|
||||
@0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1
|
||||
|
||||
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"()
|
||||
@@ -1,7 +1,7 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
|
||||
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, { ptr, ptr }, ptr, i32, i32 }
|
||||
|
||||
@main.basicTypes = global ptr null
|
||||
@"main.init$guard" = global ptr null
|
||||
@@ -17,7 +17,7 @@ _llgo_0:
|
||||
|
||||
define ptr @main.basicType(i64 %0) {
|
||||
_llgo_0:
|
||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 48)
|
||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 56)
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 0
|
||||
%3 = getelementptr inbounds i64, ptr @main.sizeBasicTypes, i64 %0
|
||||
%4 = load i64, ptr %3, align 4
|
||||
@@ -62,7 +62,7 @@ _llgo_0:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64)
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/abi.init"()
|
||||
|
||||
|
||||
25
cl/_testrt/index/in.go
Normal file
25
cl/_testrt/index/in.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import "github.com/goplus/llgo/internal/runtime/c"
|
||||
|
||||
type point struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := [...]point{{1, 2}, {3, 4}, {5, 6}}[2]
|
||||
c.Printf(c.Str("%d %d\n"), a.x, a.y)
|
||||
|
||||
b := [...][2]int{[2]int{1, 2}, [2]int{3, 4}}[1]
|
||||
c.Printf(c.Str("%d %d\n"), b[0], b[1])
|
||||
|
||||
var i int = 2
|
||||
n := [...]int{1, 2, 3, 4, 5}[i]
|
||||
c.Printf(c.Str("%d\n"), n)
|
||||
c.Printf(c.Str("%d\n"), [...]int{1, 2, 3, 4, 5}[i])
|
||||
|
||||
s := "123456"
|
||||
c.Printf(c.Str("%c\n"), s[i])
|
||||
c.Printf(c.Str("%c\n"), "123456"[1])
|
||||
}
|
||||
138
cl/_testrt/index/out.ll
Normal file
138
cl/_testrt/index/out.ll
Normal file
@@ -0,0 +1,138 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
%main.point = type { i64, i64 }
|
||||
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@0 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1
|
||||
@1 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1
|
||||
@2 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
|
||||
@3 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
|
||||
@4 = private unnamed_addr constant [4 x i8] c"%c\0A\00", align 1
|
||||
@5 = private unnamed_addr constant [7 x i8] c"123456\00", align 1
|
||||
@6 = private unnamed_addr constant [4 x i8] c"%c\0A\00", align 1
|
||||
@7 = private unnamed_addr constant [7 x i8] c"123456\00", align 1
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%0 = alloca %main.point, align 8
|
||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %0, i64 16)
|
||||
%2 = alloca [3 x %main.point], align 8
|
||||
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %2, i64 48)
|
||||
%4 = getelementptr inbounds %main.point, ptr %3, i64 0
|
||||
%5 = getelementptr inbounds %main.point, ptr %4, i32 0, i32 0
|
||||
%6 = getelementptr inbounds %main.point, ptr %4, i32 0, i32 1
|
||||
%7 = getelementptr inbounds %main.point, ptr %3, i64 1
|
||||
%8 = getelementptr inbounds %main.point, ptr %7, i32 0, i32 0
|
||||
%9 = getelementptr inbounds %main.point, ptr %7, i32 0, i32 1
|
||||
%10 = getelementptr inbounds %main.point, ptr %3, i64 2
|
||||
%11 = getelementptr inbounds %main.point, ptr %10, i32 0, i32 0
|
||||
%12 = getelementptr inbounds %main.point, ptr %10, i32 0, i32 1
|
||||
store i64 1, ptr %5, align 4
|
||||
store i64 2, ptr %6, align 4
|
||||
store i64 3, ptr %8, align 4
|
||||
store i64 4, ptr %9, align 4
|
||||
store i64 5, ptr %11, align 4
|
||||
store i64 6, ptr %12, align 4
|
||||
%13 = load [3 x %main.point], ptr %3, align 4
|
||||
%14 = getelementptr inbounds %main.point, ptr %3, i64 2
|
||||
%15 = load %main.point, ptr %14, align 4
|
||||
store %main.point %15, ptr %1, align 4
|
||||
%16 = getelementptr inbounds %main.point, ptr %1, i32 0, i32 0
|
||||
%17 = load i64, ptr %16, align 4
|
||||
%18 = getelementptr inbounds %main.point, ptr %1, i32 0, i32 1
|
||||
%19 = load i64, ptr %18, align 4
|
||||
%20 = call i32 (ptr, ...) @printf(ptr @0, i64 %17, i64 %19)
|
||||
%21 = alloca [2 x i64], align 8
|
||||
%22 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %21, i64 16)
|
||||
%23 = alloca [2 x [2 x i64]], align 8
|
||||
%24 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %23, i64 32)
|
||||
%25 = getelementptr inbounds [2 x i64], ptr %24, i64 0
|
||||
%26 = getelementptr inbounds i64, ptr %25, i64 0
|
||||
%27 = getelementptr inbounds i64, ptr %25, i64 1
|
||||
%28 = getelementptr inbounds [2 x i64], ptr %24, i64 1
|
||||
%29 = getelementptr inbounds i64, ptr %28, i64 0
|
||||
%30 = getelementptr inbounds i64, ptr %28, i64 1
|
||||
store i64 1, ptr %26, align 4
|
||||
store i64 2, ptr %27, align 4
|
||||
store i64 3, ptr %29, align 4
|
||||
store i64 4, ptr %30, align 4
|
||||
%31 = load [2 x [2 x i64]], ptr %24, align 4
|
||||
%32 = getelementptr inbounds [2 x i64], ptr %24, i64 1
|
||||
%33 = load [2 x i64], ptr %32, align 4
|
||||
store [2 x i64] %33, ptr %22, align 4
|
||||
%34 = getelementptr inbounds i64, ptr %22, i64 0
|
||||
%35 = load i64, ptr %34, align 4
|
||||
%36 = getelementptr inbounds i64, ptr %22, i64 1
|
||||
%37 = load i64, ptr %36, align 4
|
||||
%38 = call i32 (ptr, ...) @printf(ptr @1, i64 %35, i64 %37)
|
||||
%39 = alloca [5 x i64], align 8
|
||||
%40 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %39, i64 40)
|
||||
%41 = getelementptr inbounds i64, ptr %40, i64 0
|
||||
%42 = getelementptr inbounds i64, ptr %40, i64 1
|
||||
%43 = getelementptr inbounds i64, ptr %40, i64 2
|
||||
%44 = getelementptr inbounds i64, ptr %40, i64 3
|
||||
%45 = getelementptr inbounds i64, ptr %40, i64 4
|
||||
store i64 1, ptr %41, align 4
|
||||
store i64 2, ptr %42, align 4
|
||||
store i64 3, ptr %43, align 4
|
||||
store i64 4, ptr %44, align 4
|
||||
store i64 5, ptr %45, align 4
|
||||
%46 = load [5 x i64], ptr %40, align 4
|
||||
%47 = getelementptr inbounds i64, ptr %40, i64 2
|
||||
%48 = load i64, ptr %47, align 4
|
||||
%49 = call i32 (ptr, ...) @printf(ptr @2, i64 %48)
|
||||
%50 = alloca [5 x i64], align 8
|
||||
%51 = call ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr %50, i64 40)
|
||||
%52 = getelementptr inbounds i64, ptr %51, i64 0
|
||||
%53 = getelementptr inbounds i64, ptr %51, i64 1
|
||||
%54 = getelementptr inbounds i64, ptr %51, i64 2
|
||||
%55 = getelementptr inbounds i64, ptr %51, i64 3
|
||||
%56 = getelementptr inbounds i64, ptr %51, i64 4
|
||||
store i64 1, ptr %52, align 4
|
||||
store i64 2, ptr %53, align 4
|
||||
store i64 3, ptr %54, align 4
|
||||
store i64 4, ptr %55, align 4
|
||||
store i64 5, ptr %56, align 4
|
||||
%57 = load [5 x i64], ptr %51, align 4
|
||||
%58 = getelementptr inbounds i64, ptr %51, i64 2
|
||||
%59 = load i64, ptr %58, align 4
|
||||
%60 = call i32 (ptr, ...) @printf(ptr @3, i64 %59)
|
||||
%61 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @5, i64 6)
|
||||
%62 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %61)
|
||||
%63 = getelementptr inbounds i8, ptr %62, i64 2
|
||||
%64 = load i8, ptr %63, align 1
|
||||
%65 = call i32 (ptr, ...) @printf(ptr @4, i8 %64)
|
||||
%66 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @7, i64 6)
|
||||
%67 = call ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String" %66)
|
||||
%68 = getelementptr inbounds i8, ptr %67, i64 1
|
||||
%69 = load i8, ptr %68, align 1
|
||||
%70 = call i32 (ptr, ...) @printf(ptr @6, i8 %69)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.Zeroinit"(ptr, i64)
|
||||
|
||||
declare i32 @printf(ptr, ...)
|
||||
|
||||
declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64)
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.StringData"(%"github.com/goplus/llgo/internal/runtime.String")
|
||||
20
cl/_testrt/intgen/in.go
Normal file
20
cl/_testrt/intgen/in.go
Normal 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
90
cl/_testrt/intgen/out.ll
Normal 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
15
cl/_testrt/linkname/in.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/goplus/llgo/internal/runtime/c"
|
||||
)
|
||||
|
||||
//go:linkname print github.com/goplus/llgo/cl/internal/linktarget.F
|
||||
func print(a, b, c, d *c.Char)
|
||||
|
||||
func main() {
|
||||
print(c.Str("a"), c.Str("b"), c.Str("c"), c.Str("d"))
|
||||
print(c.Str("1"), c.Str("2"), c.Str("3"), c.Str("4"))
|
||||
}
|
||||
38
cl/_testrt/linkname/out.ll
Normal file
38
cl/_testrt/linkname/out.ll
Normal file
@@ -0,0 +1,38 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@0 = private unnamed_addr constant [2 x i8] c"a\00", align 1
|
||||
@1 = private unnamed_addr constant [2 x i8] c"b\00", align 1
|
||||
@2 = private unnamed_addr constant [2 x i8] c"c\00", align 1
|
||||
@3 = private unnamed_addr constant [2 x i8] c"d\00", align 1
|
||||
@4 = private unnamed_addr constant [2 x i8] c"1\00", align 1
|
||||
@5 = private unnamed_addr constant [2 x i8] c"2\00", align 1
|
||||
@6 = private unnamed_addr constant [2 x i8] c"3\00", align 1
|
||||
@7 = private unnamed_addr constant [2 x i8] c"4\00", align 1
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
call void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr @0, ptr @1, ptr @2, ptr @3)
|
||||
call void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr @4, ptr @5, ptr @6, ptr @7)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr, ptr, ptr, ptr)
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
20
cl/_testrt/qsort/in.go
Normal file
20
cl/_testrt/qsort/in.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/internal/runtime/c"
|
||||
)
|
||||
|
||||
//go:linkname qsort C.qsort
|
||||
func qsort(base c.Pointer, count, elem uintptr, compar func(a, b c.Pointer) c.Int)
|
||||
|
||||
func main() {
|
||||
a := [...]int{100, 8, 23, 2, 7}
|
||||
qsort(c.Pointer(&a[0]), 5, unsafe.Sizeof(0), func(a, b c.Pointer) c.Int {
|
||||
return c.Int(*(*int)(a) - *(*int)(b))
|
||||
})
|
||||
for _, v := range a {
|
||||
c.Printf(c.Str("%d\n"), v)
|
||||
}
|
||||
}
|
||||
71
cl/_testrt/qsort/out.ll
Normal file
71
cl/_testrt/qsort/out.ll
Normal file
@@ -0,0 +1,71 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
@"main.init$guard" = global ptr null
|
||||
@0 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 40)
|
||||
%1 = getelementptr inbounds i64, ptr %0, i64 0
|
||||
%2 = getelementptr inbounds i64, ptr %0, i64 1
|
||||
%3 = getelementptr inbounds i64, ptr %0, i64 2
|
||||
%4 = getelementptr inbounds i64, ptr %0, i64 3
|
||||
%5 = getelementptr inbounds i64, ptr %0, i64 4
|
||||
store i64 100, ptr %1, align 4
|
||||
store i64 8, ptr %2, align 4
|
||||
store i64 23, ptr %3, align 4
|
||||
store i64 2, ptr %4, align 4
|
||||
store i64 7, ptr %5, align 4
|
||||
%6 = getelementptr inbounds i64, ptr %0, i64 0
|
||||
call void @qsort(ptr %6, i64 5, i64 8, ptr @"main.main$1")
|
||||
%7 = load [5 x i64], ptr %0, align 4
|
||||
br label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_2, %_llgo_0
|
||||
%8 = phi i64 [ -1, %_llgo_0 ], [ %9, %_llgo_2 ]
|
||||
%9 = add i64 %8, 1
|
||||
%10 = icmp slt i64 %9, 5
|
||||
br i1 %10, label %_llgo_2, label %_llgo_3
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1
|
||||
%11 = getelementptr inbounds i64, ptr %0, i64 %9
|
||||
%12 = load i64, ptr %11, align 4
|
||||
%13 = call i32 (ptr, ...) @printf(ptr @0, i64 %12)
|
||||
br label %_llgo_1
|
||||
|
||||
_llgo_3: ; preds = %_llgo_1
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @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, ...)
|
||||
@@ -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
|
||||
@@ -59,15 +60,18 @@ _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"()
|
||||
|
||||
@@ -23,7 +23,7 @@ define void @main() {
|
||||
_llgo_0:
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
||||
%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
|
||||
@@ -32,7 +32,7 @@ _llgo_0:
|
||||
store i64 3, ptr %3, align 4
|
||||
%4 = getelementptr inbounds i64, ptr %0, i64 3
|
||||
store i64 4, ptr %4, align 4
|
||||
%5 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 4, i64 4)
|
||||
%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
|
||||
@@ -63,9 +63,9 @@ _llgo_3: ; preds = %_llgo_1
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64)
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||
|
||||
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr, i64, i64)
|
||||
declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64)
|
||||
|
||||
declare i32 @printf(ptr, ...)
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ define void @main() {
|
||||
_llgo_0:
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 5)
|
||||
%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
|
||||
@@ -60,4 +60,4 @@ declare void @printf(ptr, ...)
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64)
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||
|
||||
@@ -25,14 +25,55 @@ 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 ctz context
|
||||
ctz.alloca(nil, nil)
|
||||
var ctx context
|
||||
ctx.alloca(nil, nil)
|
||||
}
|
||||
|
||||
func TestCStrNoArgs(t *testing.T) {
|
||||
@@ -122,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) {
|
||||
|
||||
183
cl/compile.go
183
cl/compile.go
@@ -169,7 +169,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
|
||||
for i, n := 0, mthds.Len(); i < n; i++ {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,26 +177,43 @@ 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)
|
||||
g.Init(p.prog.Null(g.Type))
|
||||
if vtype == goVar {
|
||||
g.Init(p.prog.Null(g.Type))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) {
|
||||
sig := f.Signature
|
||||
name, ftype := p.funcName(pkgTypes, f, true)
|
||||
switch ftype {
|
||||
case ignoredFunc, llgoInstr: // llgo extended instructions
|
||||
return
|
||||
}
|
||||
if debugInstr {
|
||||
log.Println("==> NewFunc", name, "type:", sig.Recv(), sig)
|
||||
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() {
|
||||
@@ -225,6 +242,7 @@ func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa
|
||||
phi()
|
||||
}
|
||||
})
|
||||
return fn
|
||||
}
|
||||
|
||||
func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bool) llssa.BasicBlock {
|
||||
@@ -235,7 +253,8 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bo
|
||||
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
|
||||
@@ -298,6 +317,15 @@ func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||
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 {
|
||||
@@ -316,6 +344,50 @@ func (p *context) allocaCStr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr)
|
||||
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 {
|
||||
@@ -353,6 +425,8 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
||||
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:
|
||||
@@ -363,12 +437,9 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
||||
panic("todo")
|
||||
}
|
||||
default:
|
||||
panic("todo")
|
||||
/*
|
||||
fn := p.compileValue(b, cv)
|
||||
args := p.compileValues(b, call.Args, kind)
|
||||
ret = b.Call(fn, args...)
|
||||
*/
|
||||
fn := p.compileValue(b, cv)
|
||||
args := p.compileValues(b, call.Args, kind)
|
||||
ret = b.Call(fn, args...)
|
||||
}
|
||||
case *ssa.BinOp:
|
||||
x := p.compileValue(b, v.X)
|
||||
@@ -377,18 +448,6 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
||||
case *ssa.UnOp:
|
||||
x := p.compileValue(b, v.X)
|
||||
ret = b.UnOp(v.Op, x)
|
||||
case *ssa.Phi:
|
||||
phi := b.Phi(p.prog.Type(v.Type()))
|
||||
ret = phi.Expr
|
||||
p.phis = append(p.phis, func() {
|
||||
vals := p.compileValues(b, v.Edges, 0)
|
||||
preds := v.Block().Preds
|
||||
bblks := make([]llssa.BasicBlock, len(preds))
|
||||
for i, pred := range preds {
|
||||
bblks[i] = p.fn.Block(pred.Index)
|
||||
}
|
||||
phi.AddIncoming(vals, bblks)
|
||||
})
|
||||
case *ssa.ChangeType:
|
||||
t := v.Type()
|
||||
x := p.compileValue(b, v.X)
|
||||
@@ -414,6 +473,17 @@ 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
|
||||
@@ -431,13 +501,6 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
|
||||
max = p.compileValue(b, v.Max)
|
||||
}
|
||||
ret = b.Slice(x, low, high, max)
|
||||
case *ssa.MakeMap:
|
||||
var nReserve llssa.Expr
|
||||
t := v.Type()
|
||||
if v.Reserve != nil {
|
||||
nReserve = p.compileValue(b, v.Reserve)
|
||||
}
|
||||
ret = b.MakeMap(p.prog.Type(t), nReserve)
|
||||
case *ssa.MakeInterface:
|
||||
const (
|
||||
delayExpr = true // varargs: don't need to convert an expr to any
|
||||
@@ -445,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)
|
||||
@@ -504,7 +582,7 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
|
||||
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))
|
||||
@@ -524,14 +602,15 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
|
||||
}
|
||||
}
|
||||
case *ssa.Function:
|
||||
panic("unreachable")
|
||||
/*
|
||||
fn, ftype := p.funcOf(v)
|
||||
if ftype >= llgoInstrBase {
|
||||
panic("can't use llgo instruction as a value")
|
||||
}
|
||||
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
|
||||
@@ -561,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])
|
||||
@@ -616,15 +695,19 @@ 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 {
|
||||
ini()
|
||||
for len(ctx.inits) > 0 {
|
||||
inits := ctx.inits
|
||||
ctx.inits = nil
|
||||
for _, ini := range inits {
|
||||
ini()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
87
cl/import.go
87
cl/import.go
@@ -90,19 +90,14 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,21 +107,44 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) {
|
||||
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 "
|
||||
)
|
||||
@@ -134,7 +152,7 @@ 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, llgo.cstr
|
||||
if isVar || strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr
|
||||
name := pkgPath + "." + text[:idx]
|
||||
p.link[name] = link
|
||||
} else {
|
||||
@@ -182,6 +200,7 @@ const (
|
||||
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) {
|
||||
@@ -201,6 +220,20 @@ func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (s
|
||||
return name, goFunc
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -211,6 +244,8 @@ func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
|
||||
switch name {
|
||||
case "cstr":
|
||||
ftype = llgoCstr
|
||||
case "advance":
|
||||
ftype = llgoAdvance
|
||||
case "alloca":
|
||||
ftype = llgoAlloca
|
||||
case "allocaCStr":
|
||||
@@ -226,14 +261,14 @@ func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) {
|
||||
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 {
|
||||
|
||||
9
cl/internal/linktarget/foo.go
Normal file
9
cl/internal/linktarget/foo.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package linktarget
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/internal/runtime/c"
|
||||
)
|
||||
|
||||
func F(a, b *c.Char) {
|
||||
c.Printf(c.Str("a: %s, b: %s\n"), a, b)
|
||||
}
|
||||
26
cl/internal/linktarget/out.ll
Normal file
26
cl/internal/linktarget/out.ll
Normal file
@@ -0,0 +1,26 @@
|
||||
; ModuleID = 'github.com/goplus/llgo/cl/internal/linktarget'
|
||||
source_filename = "github.com/goplus/llgo/cl/internal/linktarget"
|
||||
|
||||
@"github.com/goplus/llgo/cl/internal/linktarget.init$guard" = global ptr null
|
||||
@0 = private unnamed_addr constant [14 x i8] c"a: %s, b: %s\0A\00", align 1
|
||||
|
||||
define void @"github.com/goplus/llgo/cl/internal/linktarget.F"(ptr %0, ptr %1) {
|
||||
_llgo_0:
|
||||
%2 = call i32 (ptr, ...) @printf(ptr @0, ptr %0, ptr %1)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"github.com/goplus/llgo/cl/internal/linktarget.init"() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"github.com/goplus/llgo/cl/internal/linktarget.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"github.com/goplus/llgo/cl/internal/linktarget.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i32 @printf(ptr, ...)
|
||||
2
go.mod
2
go.mod
@@ -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.3
|
||||
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
|
||||
|
||||
4
go.sum
4
go.sum
@@ -2,8 +2,8 @@ github.com/aykevl/go-wasm v0.0.1 h1:lPxy8l48P39W7I0tLrtCrLfZBOUq9IWZ7odGdyJP2AM=
|
||||
github.com/aykevl/go-wasm v0.0.1/go.mod h1:b4nggwg3lEkNKOU4wzhtLKz2q2sLxSHFnc98aGt6z/Y=
|
||||
github.com/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.3 h1:I7UkAO4kzn0Es2iHKRpGU1LjYQ452XwYfsSs1OAAXk8=
|
||||
github.com/goplus/llvm v0.7.3/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=
|
||||
|
||||
@@ -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" }
|
||||
|
||||
32
internal/aliases/aliases.go
Normal file
32
internal/aliases/aliases.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package aliases
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// Package aliases defines backward compatible shims
|
||||
// for the types.Alias type representation added in 1.22.
|
||||
// This defines placeholders for x/tools until 1.26.
|
||||
|
||||
// NewAlias creates a new TypeName in Package pkg that
|
||||
// is an alias for the type rhs.
|
||||
//
|
||||
// The enabled parameter determines whether the resulting [TypeName]'s
|
||||
// type is an [types.Alias]. Its value must be the result of a call to
|
||||
// [Enabled], which computes the effective value of
|
||||
// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled
|
||||
// function is expensive and should be called once per task (e.g.
|
||||
// package import), not once per call to NewAlias.
|
||||
func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName {
|
||||
if enabled {
|
||||
tname := types.NewTypeName(pos, pkg, name, nil)
|
||||
newAlias(tname, rhs)
|
||||
return tname
|
||||
}
|
||||
return types.NewTypeName(pos, pkg, name, rhs)
|
||||
}
|
||||
31
internal/aliases/aliases_go121.go
Normal file
31
internal/aliases/aliases_go121.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.22
|
||||
// +build !go1.22
|
||||
|
||||
package aliases
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// Alias is a placeholder for a go/types.Alias for <=1.21.
|
||||
// It will never be created by go/types.
|
||||
type Alias struct{}
|
||||
|
||||
func (*Alias) String() string { panic("unreachable") }
|
||||
func (*Alias) Underlying() types.Type { panic("unreachable") }
|
||||
func (*Alias) Obj() *types.TypeName { panic("unreachable") }
|
||||
func Rhs(alias *Alias) types.Type { panic("unreachable") }
|
||||
|
||||
// Unalias returns the type t for go <=1.21.
|
||||
func Unalias(t types.Type) types.Type { return t }
|
||||
|
||||
func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") }
|
||||
|
||||
// Enabled reports whether [NewAlias] should create [types.Alias] types.
|
||||
//
|
||||
// Before go1.22, this function always returns false.
|
||||
func Enabled() bool { return false }
|
||||
63
internal/aliases/aliases_go122.go
Normal file
63
internal/aliases/aliases_go122.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.22
|
||||
// +build go1.22
|
||||
|
||||
package aliases
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// Alias is an alias of types.Alias.
|
||||
type Alias = types.Alias
|
||||
|
||||
// Rhs returns the type on the right-hand side of the alias declaration.
|
||||
func Rhs(alias *Alias) types.Type {
|
||||
if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok {
|
||||
return alias.Rhs() // go1.23+
|
||||
}
|
||||
|
||||
// go1.22's Alias didn't have the Rhs method,
|
||||
// so Unalias is the best we can do.
|
||||
return Unalias(alias)
|
||||
}
|
||||
|
||||
// Unalias is a wrapper of types.Unalias.
|
||||
func Unalias(t types.Type) types.Type { return types.Unalias(t) }
|
||||
|
||||
// newAlias is an internal alias around types.NewAlias.
|
||||
// Direct usage is discouraged as the moment.
|
||||
// Try to use NewAlias instead.
|
||||
func newAlias(tname *types.TypeName, rhs types.Type) *Alias {
|
||||
a := types.NewAlias(tname, rhs)
|
||||
// TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect.
|
||||
Unalias(a)
|
||||
return a
|
||||
}
|
||||
|
||||
// Enabled reports whether [NewAlias] should create [types.Alias] types.
|
||||
//
|
||||
// This function is expensive! Call it sparingly.
|
||||
func Enabled() bool {
|
||||
// The only reliable way to compute the answer is to invoke go/types.
|
||||
// We don't parse the GODEBUG environment variable, because
|
||||
// (a) it's tricky to do so in a manner that is consistent
|
||||
// with the godebug package; in particular, a simple
|
||||
// substring check is not good enough. The value is a
|
||||
// rightmost-wins list of options. But more importantly:
|
||||
// (b) it is impossible to detect changes to the effective
|
||||
// setting caused by os.Setenv("GODEBUG"), as happens in
|
||||
// many tests. Therefore any attempt to cache the result
|
||||
// is just incorrect.
|
||||
fset := token.NewFileSet()
|
||||
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
|
||||
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
|
||||
_, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias)
|
||||
return enabled
|
||||
}
|
||||
@@ -27,11 +27,24 @@ 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
|
||||
|
||||
@@ -50,5 +63,11 @@ 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
|
||||
|
||||
@@ -5,7 +5,7 @@ source_filename = "github.com/goplus/llgo/internal/runtime"
|
||||
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
|
||||
%"github.com/goplus/llgo/internal/runtime.itab" = type { ptr, ptr, i32, [4 x i8], [1 x i64] }
|
||||
%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||
%"github.com/goplus/llgo/internal/abi.Type" = type { i64, i64, i32, i8, i8, i8, i8, ptr, ptr, i32, i32 }
|
||||
%"github.com/goplus/llgo/internal/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
|
||||
@@ -13,14 +13,24 @@ source_filename = "github.com/goplus/llgo/internal/runtime"
|
||||
@"github.com/goplus/llgo/internal/runtime.init$guard" = global ptr null
|
||||
@"github.com/goplus/llgo/internal/runtime.sizeBasicTypes" = global ptr null
|
||||
@0 = private unnamed_addr constant [21 x i8] c"I2Int: type mismatch\00", align 1
|
||||
@1 = private unnamed_addr constant [11 x i8] c"panic: %s\0A\00", align 1
|
||||
@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
|
||||
@@ -31,46 +41,49 @@ _llgo_0:
|
||||
define ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %0, %"github.com/goplus/llgo/internal/runtime.String" %1) {
|
||||
_llgo_0:
|
||||
%2 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8
|
||||
store %"github.com/goplus/llgo/internal/runtime.String" %1, ptr %2, align 8
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1
|
||||
%4 = load i64, ptr %3, align 4
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0
|
||||
%6 = load ptr, ptr %5, align 8
|
||||
%7 = call ptr @memcpy(ptr %0, ptr %6, i64 %4)
|
||||
%8 = getelementptr inbounds i8, ptr %0, i64 %4
|
||||
store i8 0, ptr %8, align 1
|
||||
%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
|
||||
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %1, align 8
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 1
|
||||
%3 = load i64, ptr %2, align 4
|
||||
%4 = add i64 %3, 1
|
||||
%5 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 %4)
|
||||
%6 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %1, align 8
|
||||
%7 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %5, %"github.com/goplus/llgo/internal/runtime.String" %6)
|
||||
ret ptr %7
|
||||
%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
|
||||
|
||||
@@ -81,41 +94,43 @@ _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.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 20)
|
||||
%12 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %11)
|
||||
call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %12)
|
||||
%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 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
||||
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 0
|
||||
%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
|
||||
@@ -127,17 +142,18 @@ _llgo_0:
|
||||
store i32 0, ptr %6, align 4
|
||||
store i64 0, ptr %8, align 4
|
||||
%9 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 0
|
||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 1
|
||||
store ptr %2, ptr %10, align 8
|
||||
store ptr %1, ptr %11, align 8
|
||||
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
|
||||
%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 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
||||
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||
%3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %2, i32 0, i32 0
|
||||
%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
|
||||
@@ -149,21 +165,22 @@ _llgo_0:
|
||||
store i32 0, ptr %6, align 4
|
||||
store i64 0, ptr %8, align 4
|
||||
%9 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 0
|
||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 1
|
||||
%12 = inttoptr i64 %1 to ptr
|
||||
store ptr %2, ptr %10, align 8
|
||||
store ptr %12, ptr %11, align 8
|
||||
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
|
||||
%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 = load ptr, ptr getelementptr inbounds (ptr, ptr @"github.com/goplus/llgo/internal/runtime.basicTypes", i64 24), align 8
|
||||
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
||||
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 0
|
||||
%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
|
||||
@@ -175,17 +192,18 @@ _llgo_0:
|
||||
store i32 0, ptr %7, align 4
|
||||
store i64 0, ptr %9, align 4
|
||||
%10 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 0
|
||||
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, i32 0, i32 1
|
||||
store ptr %3, ptr %11, align 8
|
||||
store ptr %1, ptr %12, align 8
|
||||
%13 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %10, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %13
|
||||
%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 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 32)
|
||||
%3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 32)
|
||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 0
|
||||
%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
|
||||
@@ -196,12 +214,13 @@ _llgo_0:
|
||||
store i32 0, ptr %6, align 4
|
||||
store i64 0, ptr %8, align 4
|
||||
%9 = alloca %"github.com/goplus/llgo/internal/runtime.iface", align 8
|
||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 0
|
||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, i32 0, i32 1
|
||||
store ptr %3, ptr %10, align 8
|
||||
store ptr %2, ptr %11, align 8
|
||||
%12 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %9, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %12
|
||||
%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
|
||||
}
|
||||
|
||||
define ptr @"github.com/goplus/llgo/internal/runtime.MakeSmallMap"() {
|
||||
@@ -213,109 +232,274 @@ _llgo_0:
|
||||
define %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr %0, i64 %1, i64 %2) {
|
||||
_llgo_0:
|
||||
%3 = alloca %"github.com/goplus/llgo/internal/runtime.Slice", align 8
|
||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 0
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 1
|
||||
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, i32 0, i32 2
|
||||
store ptr %0, ptr %4, align 8
|
||||
store i64 %1, ptr %5, align 4
|
||||
store i64 %2, ptr %6, align 4
|
||||
%7 = load %"github.com/goplus/llgo/internal/runtime.Slice", ptr %3, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.Slice" %7
|
||||
%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 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0
|
||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1
|
||||
store ptr %0, ptr %3, align 8
|
||||
store i64 %1, ptr %4, align 4
|
||||
%5 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %2, align 8
|
||||
ret %"github.com/goplus/llgo/internal/runtime.String" %5
|
||||
%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
|
||||
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %1, align 8
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 0
|
||||
%3 = load ptr, ptr %2, align 8
|
||||
ret ptr %3
|
||||
%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
|
||||
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %1, align 8
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 1
|
||||
%3 = load i64, ptr %2, align 4
|
||||
ret i64 %3
|
||||
%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
|
||||
store %"github.com/goplus/llgo/internal/runtime.String" %0, ptr %1, align 8
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 0
|
||||
%3 = load ptr, ptr %2, align 8
|
||||
ret ptr %3
|
||||
%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.Slice" %0) {
|
||||
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.Slice", align 8
|
||||
store %"github.com/goplus/llgo/internal/runtime.Slice" %0, ptr %1, align 8
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.Slice", ptr %1, i32 0, i32 1
|
||||
%3 = load i64, ptr %2, align 4
|
||||
ret i64 %3
|
||||
%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
|
||||
store %"github.com/goplus/llgo/internal/runtime.iface" %0, ptr %1, align 8
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %1, i32 0, i32 0
|
||||
%3 = load ptr, ptr %2, align 8
|
||||
%4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.itab", ptr %3, i32 0, i32 1
|
||||
%5 = load ptr, ptr %4, align 8
|
||||
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %5, i32 0, i32 6
|
||||
%7 = load i8, ptr %6, align 1
|
||||
%8 = sext i8 %7 to i64
|
||||
%9 = icmp eq i64 %8, 24
|
||||
br i1 %9, label %_llgo_2, label %_llgo_1
|
||||
%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
|
||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %1, i32 0, i32 1
|
||||
%11 = load ptr, ptr %10, align 8
|
||||
%12 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %11, i32 0, i32 1
|
||||
%13 = load i64, ptr %12, align 4
|
||||
%14 = add i64 %13, 1
|
||||
%15 = alloca i8, i64 %14, align 1
|
||||
%16 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %11, align 8
|
||||
%17 = call ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr %15, %"github.com/goplus/llgo/internal/runtime.String" %16)
|
||||
%18 = call i32 (ptr, ...) @printf(ptr @1, ptr %17)
|
||||
%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 48)
|
||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 56)
|
||||
%2 = getelementptr inbounds %"github.com/goplus/llgo/internal/abi.Type", ptr %1, i32 0, i32 0
|
||||
%3 = getelementptr inbounds i64, ptr @"github.com/goplus/llgo/internal/runtime.sizeBasicTypes", i64 %0
|
||||
%4 = load i64, ptr %3, align 4
|
||||
@@ -339,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 80)
|
||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 88)
|
||||
store ptr %1, ptr @"github.com/goplus/llgo/internal/runtime.TyAny", align 8
|
||||
store 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
|
||||
@@ -406,17 +590,34 @@ _llgo_0:
|
||||
|
||||
define ptr @"github.com/goplus/llgo/internal/runtime.makemap_small"() {
|
||||
_llgo_0:
|
||||
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 48)
|
||||
%0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 48)
|
||||
%1 = call i32 @rand()
|
||||
%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 i32 @printf(ptr, ...)
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/abi.init"()
|
||||
|
||||
declare i32 @fprintf(ptr, ptr, ...)
|
||||
|
||||
@@ -23,19 +23,33 @@ import (
|
||||
"github.com/goplus/llgo/internal/runtime/c"
|
||||
)
|
||||
|
||||
// Alloc allocates memory.
|
||||
func Alloc(size uintptr) unsafe.Pointer {
|
||||
// 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:
|
||||
s := (*String)(v.data)
|
||||
cs := c.Alloca(uintptr(s.len) + 1)
|
||||
c.Printf(c.Str("panic: %s\n"), CStrCopy(cs, *s))
|
||||
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))
|
||||
}
|
||||
|
||||
44
internal/runtime/z_closure.go
Normal file
44
internal/runtime/z_closure.go
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package runtime
|
||||
|
||||
/*
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Closure represents a closure.
|
||||
type Closure struct {
|
||||
f unsafe.Pointer
|
||||
data unsafe.Pointer // means no context if data is nil
|
||||
}
|
||||
|
||||
// NewClosure creates a closure.
|
||||
func NewClosure(f, data unsafe.Pointer) Closure {
|
||||
return Closure{f, data}
|
||||
}
|
||||
|
||||
// ClosureF returns the function of a closure.
|
||||
func ClosureF(c Closure) unsafe.Pointer {
|
||||
return c.f
|
||||
}
|
||||
|
||||
// ClosureData returns the data of a closure.
|
||||
func ClosureData(c Closure) unsafe.Pointer {
|
||||
return c.data
|
||||
}
|
||||
*/
|
||||
@@ -18,6 +18,8 @@ package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/internal/runtime/c"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -39,11 +41,30 @@ func NewSlice(data unsafe.Pointer, len, cap int) Slice {
|
||||
return Slice{data, len, cap}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -46,7 +46,7 @@ func NewString(data unsafe.Pointer, len int) String {
|
||||
}
|
||||
|
||||
// StringLen returns the length of a string.
|
||||
func StringLen(s Slice) int {
|
||||
func StringLen(s String) int {
|
||||
return s.len
|
||||
}
|
||||
|
||||
@@ -55,6 +55,15 @@ 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.
|
||||
@@ -67,8 +76,18 @@ func CStrCopy(dest unsafe.Pointer, s String) *int8 {
|
||||
}
|
||||
|
||||
func CStrDup(s String) *int8 {
|
||||
dest := Alloc(uintptr(s.len + 1))
|
||||
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}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
218
internal/typeparams/normalize.go
Normal file
218
internal/typeparams/normalize.go
Normal file
@@ -0,0 +1,218 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package typeparams
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:generate go run copytermlist.go
|
||||
|
||||
const debug = false
|
||||
|
||||
var ErrEmptyTypeSet = errors.New("empty type set")
|
||||
|
||||
// StructuralTerms returns a slice of terms representing the normalized
|
||||
// structural type restrictions of a type parameter, if any.
|
||||
//
|
||||
// Structural type restrictions of a type parameter are created via
|
||||
// non-interface types embedded in its constraint interface (directly, or via a
|
||||
// chain of interface embeddings). For example, in the declaration
|
||||
//
|
||||
// type T[P interface{~int; m()}] int
|
||||
//
|
||||
// the structural restriction of the type parameter P is ~int.
|
||||
//
|
||||
// With interface embedding and unions, the specification of structural type
|
||||
// restrictions may be arbitrarily complex. For example, consider the
|
||||
// following:
|
||||
//
|
||||
// type A interface{ ~string|~[]byte }
|
||||
//
|
||||
// type B interface{ int|string }
|
||||
//
|
||||
// type C interface { ~string|~int }
|
||||
//
|
||||
// type T[P interface{ A|B; C }] int
|
||||
//
|
||||
// In this example, the structural type restriction of P is ~string|int: A|B
|
||||
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
|
||||
// which when intersected with C (~string|~int) yields ~string|int.
|
||||
//
|
||||
// StructuralTerms computes these expansions and reductions, producing a
|
||||
// "normalized" form of the embeddings. A structural restriction is normalized
|
||||
// if it is a single union containing no interface terms, and is minimal in the
|
||||
// sense that removing any term changes the set of types satisfying the
|
||||
// constraint. It is left as a proof for the reader that, modulo sorting, there
|
||||
// is exactly one such normalized form.
|
||||
//
|
||||
// Because the minimal representation always takes this form, StructuralTerms
|
||||
// returns a slice of tilde terms corresponding to the terms of the union in
|
||||
// the normalized structural restriction. An error is returned if the
|
||||
// constraint interface is invalid, exceeds complexity bounds, or has an empty
|
||||
// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet.
|
||||
//
|
||||
// StructuralTerms makes no guarantees about the order of terms, except that it
|
||||
// is deterministic.
|
||||
func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) {
|
||||
constraint := tparam.Constraint()
|
||||
if constraint == nil {
|
||||
return nil, fmt.Errorf("%s has nil constraint", tparam)
|
||||
}
|
||||
iface, _ := constraint.Underlying().(*types.Interface)
|
||||
if iface == nil {
|
||||
return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying())
|
||||
}
|
||||
return InterfaceTermSet(iface)
|
||||
}
|
||||
|
||||
// InterfaceTermSet computes the normalized terms for a constraint interface,
|
||||
// returning an error if the term set cannot be computed or is empty. In the
|
||||
// latter case, the error will be ErrEmptyTypeSet.
|
||||
//
|
||||
// See the documentation of StructuralTerms for more information on
|
||||
// normalization.
|
||||
func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) {
|
||||
return computeTermSet(iface)
|
||||
}
|
||||
|
||||
// UnionTermSet computes the normalized terms for a union, returning an error
|
||||
// if the term set cannot be computed or is empty. In the latter case, the
|
||||
// error will be ErrEmptyTypeSet.
|
||||
//
|
||||
// See the documentation of StructuralTerms for more information on
|
||||
// normalization.
|
||||
func UnionTermSet(union *types.Union) ([]*types.Term, error) {
|
||||
return computeTermSet(union)
|
||||
}
|
||||
|
||||
func computeTermSet(typ types.Type) ([]*types.Term, error) {
|
||||
tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tset.terms.isEmpty() {
|
||||
return nil, ErrEmptyTypeSet
|
||||
}
|
||||
if tset.terms.isAll() {
|
||||
return nil, nil
|
||||
}
|
||||
var terms []*types.Term
|
||||
for _, term := range tset.terms {
|
||||
terms = append(terms, types.NewTerm(term.tilde, term.typ))
|
||||
}
|
||||
return terms, nil
|
||||
}
|
||||
|
||||
// A termSet holds the normalized set of terms for a given type.
|
||||
//
|
||||
// The name termSet is intentionally distinct from 'type set': a type set is
|
||||
// all types that implement a type (and includes method restrictions), whereas
|
||||
// a term set just represents the structural restrictions on a type.
|
||||
type termSet struct {
|
||||
complete bool
|
||||
terms termlist
|
||||
}
|
||||
|
||||
func indentf(depth int, format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...)
|
||||
}
|
||||
|
||||
func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
|
||||
if t == nil {
|
||||
panic("nil type")
|
||||
}
|
||||
|
||||
if debug {
|
||||
indentf(depth, "%s", t.String())
|
||||
defer func() {
|
||||
if err != nil {
|
||||
indentf(depth, "=> %s", err)
|
||||
} else {
|
||||
indentf(depth, "=> %s", res.terms.String())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
const maxTermCount = 100
|
||||
if tset, ok := seen[t]; ok {
|
||||
if !tset.complete {
|
||||
return nil, fmt.Errorf("cycle detected in the declaration of %s", t)
|
||||
}
|
||||
return tset, nil
|
||||
}
|
||||
|
||||
// Mark the current type as seen to avoid infinite recursion.
|
||||
tset := new(termSet)
|
||||
defer func() {
|
||||
tset.complete = true
|
||||
}()
|
||||
seen[t] = tset
|
||||
|
||||
switch u := t.Underlying().(type) {
|
||||
case *types.Interface:
|
||||
// The term set of an interface is the intersection of the term sets of its
|
||||
// embedded types.
|
||||
tset.terms = allTermlist
|
||||
for i := 0; i < u.NumEmbeddeds(); i++ {
|
||||
embedded := u.EmbeddedType(i)
|
||||
if _, ok := embedded.Underlying().(*types.TypeParam); ok {
|
||||
return nil, fmt.Errorf("invalid embedded type %T", embedded)
|
||||
}
|
||||
tset2, err := computeTermSetInternal(embedded, seen, depth+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tset.terms = tset.terms.intersect(tset2.terms)
|
||||
}
|
||||
case *types.Union:
|
||||
// The term set of a union is the union of term sets of its terms.
|
||||
tset.terms = nil
|
||||
for i := 0; i < u.Len(); i++ {
|
||||
t := u.Term(i)
|
||||
var terms termlist
|
||||
switch t.Type().Underlying().(type) {
|
||||
case *types.Interface:
|
||||
tset2, err := computeTermSetInternal(t.Type(), seen, depth+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
terms = tset2.terms
|
||||
case *types.TypeParam, *types.Union:
|
||||
// A stand-alone type parameter or union is not permitted as union
|
||||
// term.
|
||||
return nil, fmt.Errorf("invalid union term %T", t)
|
||||
default:
|
||||
if t.Type() == types.Typ[types.Invalid] {
|
||||
continue
|
||||
}
|
||||
terms = termlist{{t.Tilde(), t.Type()}}
|
||||
}
|
||||
tset.terms = tset.terms.union(terms)
|
||||
if len(tset.terms) > maxTermCount {
|
||||
return nil, fmt.Errorf("exceeded max term count %d", maxTermCount)
|
||||
}
|
||||
}
|
||||
case *types.TypeParam:
|
||||
panic("unreachable")
|
||||
default:
|
||||
// For all other types, the term set is just a single non-tilde term
|
||||
// holding the type itself.
|
||||
if u != types.Typ[types.Invalid] {
|
||||
tset.terms = termlist{{false, t}}
|
||||
}
|
||||
}
|
||||
return tset, nil
|
||||
}
|
||||
|
||||
// under is a facade for the go/types internal function of the same name. It is
|
||||
// used by typeterm.go.
|
||||
func under(t types.Type) types.Type {
|
||||
return t.Underlying()
|
||||
}
|
||||
163
internal/typeparams/termlist.go
Normal file
163
internal/typeparams/termlist.go
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by copytermlist.go DO NOT EDIT.
|
||||
|
||||
package typeparams
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// A termlist represents the type set represented by the union
|
||||
// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn.
|
||||
// A termlist is in normal form if all terms are disjoint.
|
||||
// termlist operations don't require the operands to be in
|
||||
// normal form.
|
||||
type termlist []*term
|
||||
|
||||
// allTermlist represents the set of all types.
|
||||
// It is in normal form.
|
||||
var allTermlist = termlist{new(term)}
|
||||
|
||||
// String prints the termlist exactly (without normalization).
|
||||
func (xl termlist) String() string {
|
||||
if len(xl) == 0 {
|
||||
return "∅"
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for i, x := range xl {
|
||||
if i > 0 {
|
||||
buf.WriteString(" | ")
|
||||
}
|
||||
buf.WriteString(x.String())
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// isEmpty reports whether the termlist xl represents the empty set of types.
|
||||
func (xl termlist) isEmpty() bool {
|
||||
// If there's a non-nil term, the entire list is not empty.
|
||||
// If the termlist is in normal form, this requires at most
|
||||
// one iteration.
|
||||
for _, x := range xl {
|
||||
if x != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isAll reports whether the termlist xl represents the set of all types.
|
||||
func (xl termlist) isAll() bool {
|
||||
// If there's a 𝓤 term, the entire list is 𝓤.
|
||||
// If the termlist is in normal form, this requires at most
|
||||
// one iteration.
|
||||
for _, x := range xl {
|
||||
if x != nil && x.typ == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// norm returns the normal form of xl.
|
||||
func (xl termlist) norm() termlist {
|
||||
// Quadratic algorithm, but good enough for now.
|
||||
// TODO(gri) fix asymptotic performance
|
||||
used := make([]bool, len(xl))
|
||||
var rl termlist
|
||||
for i, xi := range xl {
|
||||
if xi == nil || used[i] {
|
||||
continue
|
||||
}
|
||||
for j := i + 1; j < len(xl); j++ {
|
||||
xj := xl[j]
|
||||
if xj == nil || used[j] {
|
||||
continue
|
||||
}
|
||||
if u1, u2 := xi.union(xj); u2 == nil {
|
||||
// If we encounter a 𝓤 term, the entire list is 𝓤.
|
||||
// Exit early.
|
||||
// (Note that this is not just an optimization;
|
||||
// if we continue, we may end up with a 𝓤 term
|
||||
// and other terms and the result would not be
|
||||
// in normal form.)
|
||||
if u1.typ == nil {
|
||||
return allTermlist
|
||||
}
|
||||
xi = u1
|
||||
used[j] = true // xj is now unioned into xi - ignore it in future iterations
|
||||
}
|
||||
}
|
||||
rl = append(rl, xi)
|
||||
}
|
||||
return rl
|
||||
}
|
||||
|
||||
// union returns the union xl ∪ yl.
|
||||
func (xl termlist) union(yl termlist) termlist {
|
||||
return append(xl, yl...).norm()
|
||||
}
|
||||
|
||||
// intersect returns the intersection xl ∩ yl.
|
||||
func (xl termlist) intersect(yl termlist) termlist {
|
||||
if xl.isEmpty() || yl.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Quadratic algorithm, but good enough for now.
|
||||
// TODO(gri) fix asymptotic performance
|
||||
var rl termlist
|
||||
for _, x := range xl {
|
||||
for _, y := range yl {
|
||||
if r := x.intersect(y); r != nil {
|
||||
rl = append(rl, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rl.norm()
|
||||
}
|
||||
|
||||
// equal reports whether xl and yl represent the same type set.
|
||||
func (xl termlist) equal(yl termlist) bool {
|
||||
// TODO(gri) this should be more efficient
|
||||
return xl.subsetOf(yl) && yl.subsetOf(xl)
|
||||
}
|
||||
|
||||
// includes reports whether t ∈ xl.
|
||||
func (xl termlist) includes(t types.Type) bool {
|
||||
for _, x := range xl {
|
||||
if x.includes(t) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// supersetOf reports whether y ⊆ xl.
|
||||
func (xl termlist) supersetOf(y *term) bool {
|
||||
for _, x := range xl {
|
||||
if y.subsetOf(x) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// subsetOf reports whether xl ⊆ yl.
|
||||
func (xl termlist) subsetOf(yl termlist) bool {
|
||||
if yl.isEmpty() {
|
||||
return xl.isEmpty()
|
||||
}
|
||||
|
||||
// each term x of xl must be a subset of yl
|
||||
for _, x := range xl {
|
||||
if !yl.supersetOf(x) {
|
||||
return false // x is not a subset yl
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
169
internal/typeparams/typeterm.go
Normal file
169
internal/typeparams/typeterm.go
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by copytermlist.go DO NOT EDIT.
|
||||
|
||||
package typeparams
|
||||
|
||||
import "go/types"
|
||||
|
||||
// A term describes elementary type sets:
|
||||
//
|
||||
// ∅: (*term)(nil) == ∅ // set of no types (empty set)
|
||||
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
|
||||
// T: &term{false, T} == {T} // set of type T
|
||||
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
|
||||
type term struct {
|
||||
tilde bool // valid if typ != nil
|
||||
typ types.Type
|
||||
}
|
||||
|
||||
func (x *term) String() string {
|
||||
switch {
|
||||
case x == nil:
|
||||
return "∅"
|
||||
case x.typ == nil:
|
||||
return "𝓤"
|
||||
case x.tilde:
|
||||
return "~" + x.typ.String()
|
||||
default:
|
||||
return x.typ.String()
|
||||
}
|
||||
}
|
||||
|
||||
// equal reports whether x and y represent the same type set.
|
||||
func (x *term) equal(y *term) bool {
|
||||
// easy cases
|
||||
switch {
|
||||
case x == nil || y == nil:
|
||||
return x == y
|
||||
case x.typ == nil || y.typ == nil:
|
||||
return x.typ == y.typ
|
||||
}
|
||||
// ∅ ⊂ x, y ⊂ 𝓤
|
||||
|
||||
return x.tilde == y.tilde && types.Identical(x.typ, y.typ)
|
||||
}
|
||||
|
||||
// union returns the union x ∪ y: zero, one, or two non-nil terms.
|
||||
func (x *term) union(y *term) (_, _ *term) {
|
||||
// easy cases
|
||||
switch {
|
||||
case x == nil && y == nil:
|
||||
return nil, nil // ∅ ∪ ∅ == ∅
|
||||
case x == nil:
|
||||
return y, nil // ∅ ∪ y == y
|
||||
case y == nil:
|
||||
return x, nil // x ∪ ∅ == x
|
||||
case x.typ == nil:
|
||||
return x, nil // 𝓤 ∪ y == 𝓤
|
||||
case y.typ == nil:
|
||||
return y, nil // x ∪ 𝓤 == 𝓤
|
||||
}
|
||||
// ∅ ⊂ x, y ⊂ 𝓤
|
||||
|
||||
if x.disjoint(y) {
|
||||
return x, y // x ∪ y == (x, y) if x ∩ y == ∅
|
||||
}
|
||||
// x.typ == y.typ
|
||||
|
||||
// ~t ∪ ~t == ~t
|
||||
// ~t ∪ T == ~t
|
||||
// T ∪ ~t == ~t
|
||||
// T ∪ T == T
|
||||
if x.tilde || !y.tilde {
|
||||
return x, nil
|
||||
}
|
||||
return y, nil
|
||||
}
|
||||
|
||||
// intersect returns the intersection x ∩ y.
|
||||
func (x *term) intersect(y *term) *term {
|
||||
// easy cases
|
||||
switch {
|
||||
case x == nil || y == nil:
|
||||
return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅
|
||||
case x.typ == nil:
|
||||
return y // 𝓤 ∩ y == y
|
||||
case y.typ == nil:
|
||||
return x // x ∩ 𝓤 == x
|
||||
}
|
||||
// ∅ ⊂ x, y ⊂ 𝓤
|
||||
|
||||
if x.disjoint(y) {
|
||||
return nil // x ∩ y == ∅ if x ∩ y == ∅
|
||||
}
|
||||
// x.typ == y.typ
|
||||
|
||||
// ~t ∩ ~t == ~t
|
||||
// ~t ∩ T == T
|
||||
// T ∩ ~t == T
|
||||
// T ∩ T == T
|
||||
if !x.tilde || y.tilde {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// includes reports whether t ∈ x.
|
||||
func (x *term) includes(t types.Type) bool {
|
||||
// easy cases
|
||||
switch {
|
||||
case x == nil:
|
||||
return false // t ∈ ∅ == false
|
||||
case x.typ == nil:
|
||||
return true // t ∈ 𝓤 == true
|
||||
}
|
||||
// ∅ ⊂ x ⊂ 𝓤
|
||||
|
||||
u := t
|
||||
if x.tilde {
|
||||
u = under(u)
|
||||
}
|
||||
return types.Identical(x.typ, u)
|
||||
}
|
||||
|
||||
// subsetOf reports whether x ⊆ y.
|
||||
func (x *term) subsetOf(y *term) bool {
|
||||
// easy cases
|
||||
switch {
|
||||
case x == nil:
|
||||
return true // ∅ ⊆ y == true
|
||||
case y == nil:
|
||||
return false // x ⊆ ∅ == false since x != ∅
|
||||
case y.typ == nil:
|
||||
return true // x ⊆ 𝓤 == true
|
||||
case x.typ == nil:
|
||||
return false // 𝓤 ⊆ y == false since y != 𝓤
|
||||
}
|
||||
// ∅ ⊂ x, y ⊂ 𝓤
|
||||
|
||||
if x.disjoint(y) {
|
||||
return false // x ⊆ y == false if x ∩ y == ∅
|
||||
}
|
||||
// x.typ == y.typ
|
||||
|
||||
// ~t ⊆ ~t == true
|
||||
// ~t ⊆ T == false
|
||||
// T ⊆ ~t == true
|
||||
// T ⊆ T == true
|
||||
return !x.tilde || y.tilde
|
||||
}
|
||||
|
||||
// disjoint reports whether x ∩ y == ∅.
|
||||
// x.typ and y.typ must not be nil.
|
||||
func (x *term) disjoint(y *term) bool {
|
||||
if debug && (x.typ == nil || y.typ == nil) {
|
||||
panic("invalid argument(s)")
|
||||
}
|
||||
ux := x.typ
|
||||
if y.tilde {
|
||||
ux = under(ux)
|
||||
}
|
||||
uy := y.typ
|
||||
if x.tilde {
|
||||
uy = under(uy)
|
||||
}
|
||||
return !types.Identical(ux, uy)
|
||||
}
|
||||
525
internal/typeutil/map.go
Normal file
525
internal/typeutil/map.go
Normal file
@@ -0,0 +1,525 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package typeutil defines various utilities for types, such as Map,
|
||||
// a mapping from types.Type to interface{} values.
|
||||
package typeutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"reflect"
|
||||
|
||||
"github.com/goplus/llgo/internal/aliases"
|
||||
"github.com/goplus/llgo/internal/typeparams"
|
||||
)
|
||||
|
||||
// Map is a hash-table-based mapping from types (types.Type) to
|
||||
// arbitrary interface{} values. The concrete types that implement
|
||||
// the Type interface are pointers. Since they are not canonicalized,
|
||||
// == cannot be used to check for equivalence, and thus we cannot
|
||||
// simply use a Go map.
|
||||
//
|
||||
// Just as with map[K]V, a nil *Map is a valid empty map.
|
||||
//
|
||||
// Not thread-safe.
|
||||
type Map struct {
|
||||
hasher Hasher // shared by many Maps
|
||||
table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
|
||||
length int // number of map entries
|
||||
}
|
||||
|
||||
// entry is an entry (key/value association) in a hash bucket.
|
||||
type entry struct {
|
||||
key types.Type
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// SetHasher sets the hasher used by Map.
|
||||
//
|
||||
// All Hashers are functionally equivalent but contain internal state
|
||||
// used to cache the results of hashing previously seen types.
|
||||
//
|
||||
// A single Hasher created by MakeHasher() may be shared among many
|
||||
// Maps. This is recommended if the instances have many keys in
|
||||
// common, as it will amortize the cost of hash computation.
|
||||
//
|
||||
// A Hasher may grow without bound as new types are seen. Even when a
|
||||
// type is deleted from the map, the Hasher never shrinks, since other
|
||||
// types in the map may reference the deleted type indirectly.
|
||||
//
|
||||
// Hashers are not thread-safe, and read-only operations such as
|
||||
// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
|
||||
// read-lock) is require around all Map operations if a shared
|
||||
// hasher is accessed from multiple threads.
|
||||
//
|
||||
// If SetHasher is not called, the Map will create a private hasher at
|
||||
// the first call to Insert.
|
||||
func (m *Map) SetHasher(hasher Hasher) {
|
||||
m.hasher = hasher
|
||||
}
|
||||
|
||||
// Delete removes the entry with the given key, if any.
|
||||
// It returns true if the entry was found.
|
||||
func (m *Map) Delete(key types.Type) bool {
|
||||
if m != nil && m.table != nil {
|
||||
hash := m.hasher.Hash(key)
|
||||
bucket := m.table[hash]
|
||||
for i, e := range bucket {
|
||||
if e.key != nil && types.Identical(key, e.key) {
|
||||
// We can't compact the bucket as it
|
||||
// would disturb iterators.
|
||||
bucket[i] = entry{}
|
||||
m.length--
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// At returns the map entry for the given key.
|
||||
// The result is nil if the entry is not present.
|
||||
func (m *Map) At(key types.Type) interface{} {
|
||||
if m != nil && m.table != nil {
|
||||
for _, e := range m.table[m.hasher.Hash(key)] {
|
||||
if e.key != nil && types.Identical(key, e.key) {
|
||||
return e.value
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set sets the map entry for key to val,
|
||||
// and returns the previous entry, if any.
|
||||
func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
|
||||
if m.table != nil {
|
||||
hash := m.hasher.Hash(key)
|
||||
bucket := m.table[hash]
|
||||
var hole *entry
|
||||
for i, e := range bucket {
|
||||
if e.key == nil {
|
||||
hole = &bucket[i]
|
||||
} else if types.Identical(key, e.key) {
|
||||
prev = e.value
|
||||
bucket[i].value = value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if hole != nil {
|
||||
*hole = entry{key, value} // overwrite deleted entry
|
||||
} else {
|
||||
m.table[hash] = append(bucket, entry{key, value})
|
||||
}
|
||||
} else {
|
||||
if m.hasher.memo == nil {
|
||||
m.hasher = MakeHasher()
|
||||
}
|
||||
hash := m.hasher.Hash(key)
|
||||
m.table = map[uint32][]entry{hash: {entry{key, value}}}
|
||||
}
|
||||
|
||||
m.length++
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the number of map entries.
|
||||
func (m *Map) Len() int {
|
||||
if m != nil {
|
||||
return m.length
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Iterate calls function f on each entry in the map in unspecified order.
|
||||
//
|
||||
// If f should mutate the map, Iterate provides the same guarantees as
|
||||
// Go maps: if f deletes a map entry that Iterate has not yet reached,
|
||||
// f will not be invoked for it, but if f inserts a map entry that
|
||||
// Iterate has not yet reached, whether or not f will be invoked for
|
||||
// it is unspecified.
|
||||
func (m *Map) Iterate(f func(key types.Type, value interface{})) {
|
||||
if m != nil {
|
||||
for _, bucket := range m.table {
|
||||
for _, e := range bucket {
|
||||
if e.key != nil {
|
||||
f(e.key, e.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keys returns a new slice containing the set of map keys.
|
||||
// The order is unspecified.
|
||||
func (m *Map) Keys() []types.Type {
|
||||
keys := make([]types.Type, 0, m.Len())
|
||||
m.Iterate(func(key types.Type, _ interface{}) {
|
||||
keys = append(keys, key)
|
||||
})
|
||||
return keys
|
||||
}
|
||||
|
||||
func (m *Map) toString(values bool) string {
|
||||
if m == nil {
|
||||
return "{}"
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "{")
|
||||
sep := ""
|
||||
m.Iterate(func(key types.Type, value interface{}) {
|
||||
fmt.Fprint(&buf, sep)
|
||||
sep = ", "
|
||||
fmt.Fprint(&buf, key)
|
||||
if values {
|
||||
fmt.Fprintf(&buf, ": %q", value)
|
||||
}
|
||||
})
|
||||
fmt.Fprint(&buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// String returns a string representation of the map's entries.
|
||||
// Values are printed using fmt.Sprintf("%v", v).
|
||||
// Order is unspecified.
|
||||
func (m *Map) String() string {
|
||||
return m.toString(true)
|
||||
}
|
||||
|
||||
// KeysString returns a string representation of the map's key set.
|
||||
// Order is unspecified.
|
||||
func (m *Map) KeysString() string {
|
||||
return m.toString(false)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Hasher
|
||||
|
||||
// A Hasher maps each type to its hash value.
|
||||
// For efficiency, a hasher uses memoization; thus its memory
|
||||
// footprint grows monotonically over time.
|
||||
// Hashers are not thread-safe.
|
||||
// Hashers have reference semantics.
|
||||
// Call MakeHasher to create a Hasher.
|
||||
type Hasher struct {
|
||||
memo map[types.Type]uint32
|
||||
|
||||
// ptrMap records pointer identity.
|
||||
ptrMap map[interface{}]uint32
|
||||
|
||||
// sigTParams holds type parameters from the signature being hashed.
|
||||
// Signatures are considered identical modulo renaming of type parameters, so
|
||||
// within the scope of a signature type the identity of the signature's type
|
||||
// parameters is just their index.
|
||||
//
|
||||
// Since the language does not currently support referring to uninstantiated
|
||||
// generic types or functions, and instantiated signatures do not have type
|
||||
// parameter lists, we should never encounter a second non-empty type
|
||||
// parameter list when hashing a generic signature.
|
||||
sigTParams *types.TypeParamList
|
||||
}
|
||||
|
||||
// MakeHasher returns a new Hasher instance.
|
||||
func MakeHasher() Hasher {
|
||||
return Hasher{
|
||||
memo: make(map[types.Type]uint32),
|
||||
ptrMap: make(map[interface{}]uint32),
|
||||
sigTParams: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// Hash computes a hash value for the given type t such that
|
||||
// Identical(t, t') => Hash(t) == Hash(t').
|
||||
func (h Hasher) Hash(t types.Type) uint32 {
|
||||
hash, ok := h.memo[t]
|
||||
if !ok {
|
||||
hash = h.hashFor(t)
|
||||
h.memo[t] = hash
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
// hashString computes the Fowler–Noll–Vo hash of s.
|
||||
func hashString(s string) uint32 {
|
||||
var h uint32
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint32(s[i])
|
||||
h *= 16777619
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func HashSig(h Hasher, t *types.Signature) uint32 {
|
||||
var hash uint32 = 9091
|
||||
if t.Variadic() {
|
||||
hash *= 8863
|
||||
}
|
||||
|
||||
// Use a separate hasher for types inside of the signature, where type
|
||||
// parameter identity is modified to be (index, constraint). We must use a
|
||||
// new memo for this hasher as type identity may be affected by this
|
||||
// masking. For example, in func[T any](*T), the identity of *T depends on
|
||||
// whether we are mapping the argument in isolation, or recursively as part
|
||||
// of hashing the signature.
|
||||
//
|
||||
// We should never encounter a generic signature while hashing another
|
||||
// generic signature, but defensively set sigTParams only if h.mask is
|
||||
// unset.
|
||||
tparams := t.TypeParams()
|
||||
if h.sigTParams == nil && tparams.Len() != 0 {
|
||||
h = Hasher{
|
||||
// There may be something more efficient than discarding the existing
|
||||
// memo, but it would require detecting whether types are 'tainted' by
|
||||
// references to type parameters.
|
||||
memo: make(map[types.Type]uint32),
|
||||
// Re-using ptrMap ensures that pointer identity is preserved in this
|
||||
// hasher.
|
||||
ptrMap: h.ptrMap,
|
||||
sigTParams: tparams,
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < tparams.Len(); i++ {
|
||||
tparam := tparams.At(i)
|
||||
hash += 7 * h.Hash(tparam.Constraint())
|
||||
}
|
||||
|
||||
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
|
||||
}
|
||||
|
||||
// hashFor computes the hash of t.
|
||||
func (h Hasher) hashFor(t types.Type) uint32 {
|
||||
// See Identical for rationale.
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
return uint32(t.Kind())
|
||||
|
||||
case *aliases.Alias:
|
||||
return h.Hash(t.Underlying())
|
||||
|
||||
case *types.Array:
|
||||
return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
|
||||
|
||||
case *types.Slice:
|
||||
return 9049 + 2*h.Hash(t.Elem())
|
||||
|
||||
case *types.Struct:
|
||||
var hash uint32 = 9059
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
f := t.Field(i)
|
||||
if f.Anonymous() {
|
||||
hash += 8861
|
||||
}
|
||||
hash += hashString(t.Tag(i))
|
||||
hash += hashString(f.Name()) // (ignore f.Pkg)
|
||||
hash += h.Hash(f.Type())
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.Pointer:
|
||||
return 9067 + 2*h.Hash(t.Elem())
|
||||
|
||||
case *types.Signature:
|
||||
return HashSig(h, t)
|
||||
|
||||
case *types.Union:
|
||||
return h.hashUnion(t)
|
||||
|
||||
case *types.Interface:
|
||||
// Interfaces are identical if they have the same set of methods, with
|
||||
// identical names and types, and they have the same set of type
|
||||
// restrictions. See go/types.identical for more details.
|
||||
var hash uint32 = 9103
|
||||
|
||||
// Hash methods.
|
||||
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||
// Method order is not significant.
|
||||
// Ignore m.Pkg().
|
||||
m := t.Method(i)
|
||||
// Use shallow hash on method signature to
|
||||
// avoid anonymous interface cycles.
|
||||
hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type())
|
||||
}
|
||||
|
||||
// Hash type restrictions.
|
||||
terms, err := typeparams.InterfaceTermSet(t)
|
||||
// if err != nil t has invalid type restrictions.
|
||||
if err == nil {
|
||||
hash += h.hashTermSet(terms)
|
||||
}
|
||||
|
||||
return hash
|
||||
|
||||
case *types.Map:
|
||||
return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
|
||||
|
||||
case *types.Chan:
|
||||
return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
|
||||
|
||||
case *types.Named:
|
||||
hash := h.hashPtr(t.Obj())
|
||||
targs := t.TypeArgs()
|
||||
for i := 0; i < targs.Len(); i++ {
|
||||
targ := targs.At(i)
|
||||
hash += 2 * h.Hash(targ)
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.TypeParam:
|
||||
return h.hashTypeParam(t)
|
||||
|
||||
case *types.Tuple:
|
||||
return h.hashTuple(t)
|
||||
|
||||
case interface{ Hash(h Hasher) uint32 }:
|
||||
return t.Hash(h)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("%T: %v", t, t))
|
||||
}
|
||||
|
||||
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
|
||||
// See go/types.identicalTypes for rationale.
|
||||
n := tuple.Len()
|
||||
hash := 9137 + 2*uint32(n)
|
||||
for i := 0; i < n; i++ {
|
||||
hash += 3 * h.Hash(tuple.At(i).Type())
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
func (h Hasher) hashUnion(t *types.Union) uint32 {
|
||||
// Hash type restrictions.
|
||||
terms, err := typeparams.UnionTermSet(t)
|
||||
// if err != nil t has invalid type restrictions. Fall back on a non-zero
|
||||
// hash.
|
||||
if err != nil {
|
||||
return 9151
|
||||
}
|
||||
return h.hashTermSet(terms)
|
||||
}
|
||||
|
||||
func (h Hasher) hashTermSet(terms []*types.Term) uint32 {
|
||||
hash := 9157 + 2*uint32(len(terms))
|
||||
for _, term := range terms {
|
||||
// term order is not significant.
|
||||
termHash := h.Hash(term.Type())
|
||||
if term.Tilde() {
|
||||
termHash *= 9161
|
||||
}
|
||||
hash += 3 * termHash
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
// hashTypeParam returns a hash of the type parameter t, with a hash value
|
||||
// depending on whether t is contained in h.sigTParams.
|
||||
//
|
||||
// If h.sigTParams is set and contains t, then we are in the process of hashing
|
||||
// a signature, and the hash value of t must depend only on t's index and
|
||||
// constraint: signatures are considered identical modulo type parameter
|
||||
// renaming. To avoid infinite recursion, we only hash the type parameter
|
||||
// index, and rely on types.Identical to handle signatures where constraints
|
||||
// are not identical.
|
||||
//
|
||||
// Otherwise the hash of t depends only on t's pointer identity.
|
||||
func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 {
|
||||
if h.sigTParams != nil {
|
||||
i := t.Index()
|
||||
if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) {
|
||||
return 9173 + 3*uint32(i)
|
||||
}
|
||||
}
|
||||
return h.hashPtr(t.Obj())
|
||||
}
|
||||
|
||||
// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that
|
||||
// pointers values are not dependent on the GC.
|
||||
func (h Hasher) hashPtr(ptr interface{}) uint32 {
|
||||
if hash, ok := h.ptrMap[ptr]; ok {
|
||||
return hash
|
||||
}
|
||||
hash := uint32(reflect.ValueOf(ptr).Pointer())
|
||||
h.ptrMap[ptr] = hash
|
||||
return hash
|
||||
}
|
||||
|
||||
// shallowHash computes a hash of t without looking at any of its
|
||||
// element Types, to avoid potential anonymous cycles in the types of
|
||||
// interface methods.
|
||||
//
|
||||
// When an unnamed non-empty interface type appears anywhere among the
|
||||
// arguments or results of an interface method, there is a potential
|
||||
// for endless recursion. Consider:
|
||||
//
|
||||
// type X interface { m() []*interface { X } }
|
||||
//
|
||||
// The problem is that the Methods of the interface in m's result type
|
||||
// include m itself; there is no mention of the named type X that
|
||||
// might help us break the cycle.
|
||||
// (See comment in go/types.identical, case *Interface, for more.)
|
||||
func (h Hasher) shallowHash(t types.Type) uint32 {
|
||||
// t is the type of an interface method (Signature),
|
||||
// its params or results (Tuples), or their immediate
|
||||
// elements (mostly Slice, Pointer, Basic, Named),
|
||||
// so there's no need to optimize anything else.
|
||||
switch t := t.(type) {
|
||||
case *aliases.Alias:
|
||||
return h.shallowHash(t.Underlying())
|
||||
|
||||
case *types.Signature:
|
||||
var hash uint32 = 604171
|
||||
if t.Variadic() {
|
||||
hash *= 971767
|
||||
}
|
||||
// The Signature/Tuple recursion is always finite
|
||||
// and invariably shallow.
|
||||
return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results())
|
||||
|
||||
case *types.Tuple:
|
||||
n := t.Len()
|
||||
hash := 9137 + 2*uint32(n)
|
||||
for i := 0; i < n; i++ {
|
||||
hash += 53471161 * h.shallowHash(t.At(i).Type())
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.Basic:
|
||||
return 45212177 * uint32(t.Kind())
|
||||
|
||||
case *types.Array:
|
||||
return 1524181 + 2*uint32(t.Len())
|
||||
|
||||
case *types.Slice:
|
||||
return 2690201
|
||||
|
||||
case *types.Struct:
|
||||
return 3326489
|
||||
|
||||
case *types.Pointer:
|
||||
return 4393139
|
||||
|
||||
case *types.Union:
|
||||
return 562448657
|
||||
|
||||
case *types.Interface:
|
||||
return 2124679 // no recursion here
|
||||
|
||||
case *types.Map:
|
||||
return 9109
|
||||
|
||||
case *types.Chan:
|
||||
return 9127
|
||||
|
||||
case *types.Named:
|
||||
return h.hashPtr(t.Obj())
|
||||
|
||||
case *types.TypeParam:
|
||||
return h.hashPtr(t.Obj())
|
||||
}
|
||||
panic(fmt.Sprintf("shallowHash: %T: %v", t, t))
|
||||
}
|
||||
@@ -17,9 +17,12 @@
|
||||
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 TestFromTestrt(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)
|
||||
}
|
||||
}
|
||||
|
||||
344
ssa/expr.go
344
ssa/expr.go
@@ -49,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}}
|
||||
@@ -73,6 +79,25 @@ func (p delayExprTy) String() string {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type phisExprTy struct {
|
||||
phis []llvm.Value
|
||||
Type
|
||||
}
|
||||
|
||||
func (p phisExprTy) Underlying() types.Type {
|
||||
panic("don't call")
|
||||
}
|
||||
|
||||
func (p phisExprTy) String() string {
|
||||
return "phisExpr"
|
||||
}
|
||||
|
||||
func phisExpr(t Type, phis []llvm.Value) Expr {
|
||||
return Expr{Type: &aType{t: &phisExprTy{phis, t}, kind: vkPhisExpr}}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Null returns a null constant expression.
|
||||
func (p Program) Null(t Type) Expr {
|
||||
return Expr{llvm.ConstNull(t.ll), t}
|
||||
@@ -148,6 +173,13 @@ func (b Builder) Const(v constant.Value, typ Type) Expr {
|
||||
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()}
|
||||
@@ -268,12 +300,17 @@ 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")
|
||||
}
|
||||
idx := mathOpIdx(op, kind)
|
||||
if llop := mathOpToLLVM[idx]; llop != 0 {
|
||||
return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type}
|
||||
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 {
|
||||
@@ -324,14 +361,37 @@ func (b Builder) UnOp(op token.Token, x Expr) Expr {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func llvmValues(vals []Expr) []llvm.Value {
|
||||
func checkExpr(v Expr, t types.Type, b Builder) Expr {
|
||||
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 {
|
||||
@@ -346,19 +406,63 @@ type Phi struct {
|
||||
}
|
||||
|
||||
// AddIncoming adds incoming values to a phi node.
|
||||
func (p Phi) AddIncoming(vals []Expr, bblks []BasicBlock) {
|
||||
v := llvmValues(vals)
|
||||
b := llvmBlocks(bblks)
|
||||
p.impl.AddIncoming(v, b)
|
||||
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 {
|
||||
return Phi{Expr{llvm.CreatePHI(b.impl, t.ll), t}}
|
||||
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 {
|
||||
@@ -377,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
|
||||
@@ -387,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]
|
||||
@@ -407,6 +519,15 @@ func (b Builder) FieldAddr(x Expr, idx int) Expr {
|
||||
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.
|
||||
//
|
||||
@@ -438,6 +559,44 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
|
||||
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.
|
||||
//
|
||||
@@ -447,10 +606,6 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
|
||||
// Type() returns string if the type of X was string, otherwise a
|
||||
// *types.Slice with the same element type as X.
|
||||
//
|
||||
// Pos() returns the ast.SliceExpr.Lbrack if created by a x[:] slice
|
||||
// operation, the ast.CompositeLit.Lbrace if created by a literal, or
|
||||
// NoPos if not explicit in the source (e.g. a variadic argument slice).
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = slice t0[1:]
|
||||
@@ -460,20 +615,50 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
|
||||
}
|
||||
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:
|
||||
ret.Type = prog.Type(types.NewSlice(te.Elem()))
|
||||
if low.IsNil() && high.IsNil() && max.IsNil() {
|
||||
n := prog.Val(int(te.Len()))
|
||||
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice"), x, n, n).impl
|
||||
return ret
|
||||
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
|
||||
}
|
||||
}
|
||||
panic("todo")
|
||||
if max.IsNil() {
|
||||
max = nCap
|
||||
}
|
||||
ret.impl = b.InlineCall(pkg.rtFunc("NewSlice3"), base, nEltSize, nCap, low, high, max).impl
|
||||
return
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -483,9 +668,6 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) {
|
||||
//
|
||||
// t is a (possibly named) *types.Map.
|
||||
//
|
||||
// Pos() returns the ast.CallExpr.Lparen, if created by make(map), or
|
||||
// the ast.CompositeLit.Lbrack if created by a literal.
|
||||
//
|
||||
// Example printed form:
|
||||
//
|
||||
// t1 = make map[string]int t0
|
||||
@@ -501,6 +683,36 @@ func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) {
|
||||
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,
|
||||
@@ -527,15 +739,15 @@ func (b Builder) Alloc(t *types.Pointer, heap bool) (ret Expr) {
|
||||
log.Printf("Alloc %v, %v\n", t, heap)
|
||||
}
|
||||
prog := b.Prog
|
||||
telem := t.Elem()
|
||||
pkg := b.fn.pkg
|
||||
elem := prog.Type(t.Elem())
|
||||
size := b.SizeOf(elem)
|
||||
if heap {
|
||||
pkg := b.fn.pkg
|
||||
size := prog.sizs.Sizeof(telem)
|
||||
ret = b.Call(pkg.rtFunc("Alloc"), prog.Val(uintptr(size)))
|
||||
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
|
||||
}
|
||||
@@ -637,9 +849,6 @@ 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)
|
||||
@@ -692,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)
|
||||
@@ -813,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
|
||||
}
|
||||
|
||||
@@ -842,9 +1068,21 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
|
||||
case "len":
|
||||
if len(args) == 1 {
|
||||
arg := args[0]
|
||||
switch arg.t.Underlying().(type) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,10 @@
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"go/types"
|
||||
"runtime"
|
||||
|
||||
"github.com/goplus/llgo/internal/typeutil"
|
||||
"github.com/goplus/llvm"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -98,7 +96,7 @@ func Initialize(flags InitFlags) {
|
||||
type aProgram struct {
|
||||
ctx llvm.Context
|
||||
typs typeutil.Map
|
||||
sizs types.Sizes
|
||||
// sizs types.Sizes
|
||||
|
||||
rt *types.Package
|
||||
rtget func() *types.Package
|
||||
@@ -123,6 +121,7 @@ type aProgram struct {
|
||||
|
||||
anyTy Type
|
||||
voidTy Type
|
||||
voidPtr Type
|
||||
boolTy Type
|
||||
cstrTy Type
|
||||
stringTy Type
|
||||
@@ -141,16 +140,19 @@ func NewProgram(target *Target) Program {
|
||||
if target == nil {
|
||||
target = &Target{}
|
||||
}
|
||||
arch := target.GOARCH
|
||||
if arch == "" {
|
||||
arch = runtime.GOARCH
|
||||
}
|
||||
ctx := llvm.NewContext()
|
||||
sizes := types.SizesFor("gc", arch)
|
||||
// TODO(xsw): Finalize may cause panic, so comment it.
|
||||
// ctx.Finalize()
|
||||
td := llvm.NewTargetData("") // TODO(xsw): target config
|
||||
return &aProgram{ctx: ctx, sizs: sizes, target: target, td: td}
|
||||
/*
|
||||
arch := target.GOARCH
|
||||
if arch == "" {
|
||||
arch = runtime.GOARCH
|
||||
}
|
||||
sizes := types.SizesFor("gc", arch)
|
||||
|
||||
// TODO(xsw): Finalize may cause panic, so comment it.
|
||||
ctx.Finalize()
|
||||
*/
|
||||
return &aProgram{ctx: ctx, target: target, td: td}
|
||||
}
|
||||
|
||||
// SetRuntime sets the runtime.
|
||||
@@ -232,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 {
|
||||
@@ -305,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 {
|
||||
@@ -329,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
|
||||
|
||||
@@ -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?")
|
||||
}
|
||||
}()
|
||||
a.Underlying()
|
||||
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) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package ssa
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"log"
|
||||
|
||||
"github.com/goplus/llvm"
|
||||
@@ -102,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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
108
ssa/type.go
108
ssa/type.go
@@ -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,21 @@ 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))
|
||||
}
|
||||
@@ -123,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)
|
||||
}
|
||||
@@ -138,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 {
|
||||
@@ -261,6 +252,10 @@ func (p Program) toLLVMType(typ types.Type) Type {
|
||||
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}
|
||||
@@ -306,29 +301,42 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) {
|
||||
return
|
||||
}
|
||||
|
||||
func (p Program) toLLVMFunc(sig *types.Signature, isPtr bool) Type {
|
||||
tParams := sig.Params()
|
||||
n := tParams.Len()
|
||||
hasVArg := HasVArg(tParams, n)
|
||||
if hasVArg {
|
||||
n--
|
||||
func (p Program) toLLVMFunc(sig *types.Signature, inC, isDecl bool) Type {
|
||||
if isDecl || inC {
|
||||
tParams := sig.Params()
|
||||
n := tParams.Len()
|
||||
hasVArg := HasVArg(tParams, n)
|
||||
if hasVArg {
|
||||
n--
|
||||
}
|
||||
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()
|
||||
case 1:
|
||||
ret = p.Type(out.At(0).Type()).ll
|
||||
default:
|
||||
ret = p.toLLVMTuple(out)
|
||||
}
|
||||
ft := llvm.FunctionType(ret, params, hasVArg)
|
||||
if isDecl {
|
||||
kind = vkFuncDecl
|
||||
} else {
|
||||
ft = llvm.PointerType(ft, 0)
|
||||
kind = vkFuncPtr
|
||||
}
|
||||
return &aType{ft, sig, kind}
|
||||
}
|
||||
params := p.toLLVMTypes(tParams, n)
|
||||
out := sig.Results()
|
||||
var ret llvm.Type
|
||||
switch nret := out.Len(); nret {
|
||||
case 0:
|
||||
ret = p.tyVoid()
|
||||
case 1:
|
||||
ret = p.Type(out.At(0).Type()).ll
|
||||
default:
|
||||
ret = p.toLLVMTuple(out)
|
||||
flds := []*types.Var{
|
||||
types.NewField(token.NoPos, nil, "f", (*CFuncPtr)(sig), false),
|
||||
types.NewField(token.NoPos, nil, "data", types.Typ[types.UnsafePointer], false),
|
||||
}
|
||||
ft := llvm.FunctionType(ret, params, hasVArg)
|
||||
if isPtr {
|
||||
ft = llvm.PointerType(ft, 0)
|
||||
}
|
||||
return &aType{ft, sig, vkFunc}
|
||||
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
140
ssa/type_c.go
Normal 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
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user