diff --git a/cl/_testdata/print/out.ll b/cl/_testdata/print/out.ll index 27462d72..800dd205 100644 --- a/cl/_testdata/print/out.ll +++ b/cl/_testdata/print/out.ll @@ -847,7 +847,7 @@ _llgo_3: ; preds = %_llgo_0 define void @main.printfloat(double %0) { _llgo_0: - %1 = fcmp one double %0, %0 + %1 = fcmp une double %0, %0 br i1 %1, label %_llgo_1, label %_llgo_3 _llgo_1: ; preds = %_llgo_0 diff --git a/cl/_testrt/builtin/in.go b/cl/_testrt/builtin/in.go index e16f2465..86780665 100644 --- a/cl/_testrt/builtin/in.go +++ b/cl/_testrt/builtin/in.go @@ -40,7 +40,53 @@ func main() { println(n) } println(demo, fn1, fn2, fn3) + + for i, v := range "中abcd" { + println(i, v) + } + + println(Inf(1), Inf(-1), NaN(), IsNaN(NaN()), IsNaN(1.0)) + + data1 := []byte("中abcd") + data2 := []rune("中abcd") + println(data1, data2) + println(string(data1), string(data2), string(data1[3]), string(data2[0])) + s1 := "abc" + s2 := "abd" + println(s1 == "abc", s1 == s2, s1 != s2, s1 < s2, s1 <= s2, s1 > s2, s1 >= s2) } func demo() { } + +const ( + uvnan = 0x7FF8000000000001 + uvinf = 0x7FF0000000000000 + uvneginf = 0xFFF0000000000000 + uvone = 0x3FF0000000000000 + mask = 0x7FF + shift = 64 - 11 - 1 + bias = 1023 + signMask = 1 << 63 + fracMask = 1<= 0, negative infinity if sign < 0. +func Inf(sign int) float64 { + var v uint64 + if sign >= 0 { + v = uvinf + } else { + v = uvneginf + } + return Float64frombits(v) +} + +// NaN returns an IEEE 754 “not-a-number” value. +func NaN() float64 { return Float64frombits(uvnan) } + +func IsNaN(f float64) (is bool) { + return f != f +} + +func Float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) } diff --git a/cl/_testrt/builtin/out.ll b/cl/_testrt/builtin/out.ll index d6c806f3..dfc39090 100644 --- a/cl/_testrt/builtin/out.ll +++ b/cl/_testrt/builtin/out.ll @@ -17,7 +17,61 @@ source_filename = "main" @3 = private unnamed_addr constant [6 x i8] c"hello\00", align 1 @4 = private unnamed_addr constant [4 x i8] c"def\00", align 1 @5 = private unnamed_addr constant [5 x i8] c"ABCD\00", align 1 -@6 = private unnamed_addr constant [3 x i8] c"fn\00", align 1 +@6 = private unnamed_addr constant [8 x i8] c"\E4\B8\ADabcd\00", align 1 +@7 = private unnamed_addr constant [8 x i8] c"\E4\B8\ADabcd\00", align 1 +@8 = private unnamed_addr constant [8 x i8] c"\E4\B8\ADabcd\00", align 1 +@9 = private unnamed_addr constant [4 x i8] c"abc\00", align 1 +@10 = private unnamed_addr constant [4 x i8] c"abc\00", align 1 +@11 = private unnamed_addr constant [4 x i8] c"abc\00", align 1 +@12 = private unnamed_addr constant [4 x i8] c"abd\00", align 1 +@13 = private unnamed_addr constant [4 x i8] c"abc\00", align 1 +@14 = private unnamed_addr constant [4 x i8] c"abd\00", align 1 +@15 = private unnamed_addr constant [4 x i8] c"abc\00", align 1 +@16 = private unnamed_addr constant [4 x i8] c"abd\00", align 1 +@17 = private unnamed_addr constant [4 x i8] c"abc\00", align 1 +@18 = private unnamed_addr constant [4 x i8] c"abd\00", align 1 +@19 = private unnamed_addr constant [4 x i8] c"abc\00", align 1 +@20 = private unnamed_addr constant [4 x i8] c"abd\00", align 1 +@21 = private unnamed_addr constant [4 x i8] c"abc\00", align 1 +@22 = private unnamed_addr constant [4 x i8] c"abd\00", align 1 +@23 = private unnamed_addr constant [3 x i8] c"fn\00", align 1 + +define double @main.Float64frombits(i64 %0) { +_llgo_0: + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) + store i64 %0, ptr %1, align 4 + %2 = load double, ptr %1, align 8 + ret double %2 +} + +define double @main.Inf(i64 %0) { +_llgo_0: + %1 = icmp sge i64 %0, 0 + br i1 %1, label %_llgo_1, label %_llgo_3 + +_llgo_1: ; preds = %_llgo_0 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_3, %_llgo_1 + %2 = phi i64 [ 9218868437227405312, %_llgo_1 ], [ -4503599627370496, %_llgo_3 ] + %3 = call double @main.Float64frombits(i64 %2) + ret double %3 + +_llgo_3: ; preds = %_llgo_0 + br label %_llgo_2 +} + +define i1 @main.IsNaN(double %0) { +_llgo_0: + %1 = fcmp une double %0, %0 + ret i1 %1 +} + +define double @main.NaN() { +_llgo_0: + %0 = call double @main.Float64frombits(i64 9221120237041090561) + ret double %0 +} define void @main.demo() { _llgo_0: @@ -380,13 +434,199 @@ _llgo_0: %175 = extractvalue { ptr, ptr } %174, 0 call void @"github.com/goplus/llgo/internal/runtime.PrintPointer"(ptr %175) call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %176 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %177 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %176, i32 0, i32 0 + store ptr @6, ptr %177, align 8 + %178 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %176, i32 0, i32 1 + store i64 7, ptr %178, align 4 + %179 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %176, align 8 + %180 = call ptr @"github.com/goplus/llgo/internal/runtime.NewStringIter"(%"github.com/goplus/llgo/internal/runtime.String" %179) + br label %_llgo_1 + +_llgo_1: ; preds = %_llgo_2, %_llgo_0 + %181 = call { i1, i64, i32 } @"github.com/goplus/llgo/internal/runtime.StringIterNext"(ptr %180) + %182 = extractvalue { i1, i64, i32 } %181, 0 + br i1 %182, label %_llgo_2, label %_llgo_3 + +_llgo_2: ; preds = %_llgo_1 + %183 = extractvalue { i1, i64, i32 } %181, 1 + %184 = extractvalue { i1, i64, i32 } %181, 2 + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %183) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + %185 = sext i32 %184 to i64 + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %185) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_1 + +_llgo_3: ; preds = %_llgo_1 + %186 = call double @main.Inf(i64 1) + %187 = call double @main.Inf(i64 -1) + %188 = call double @main.NaN() + %189 = call double @main.NaN() + %190 = call i1 @main.IsNaN(double %189) + %191 = call i1 @main.IsNaN(double 1.000000e+00) + call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %186) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %187) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintFloat"(double %188) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %190) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %191) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %192 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %193 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %192, i32 0, i32 0 + store ptr @7, ptr %193, align 8 + %194 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %192, i32 0, i32 1 + store i64 7, ptr %194, align 4 + %195 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %192, align 8 + %196 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.StringToBytes"(%"github.com/goplus/llgo/internal/runtime.String" %195) + %197 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %198 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %197, i32 0, i32 0 + store ptr @8, ptr %198, align 8 + %199 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %197, i32 0, i32 1 + store i64 7, ptr %199, align 4 + %200 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %197, align 8 + %201 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.StringToRunes"(%"github.com/goplus/llgo/internal/runtime.String" %200) + call void @"github.com/goplus/llgo/internal/runtime.PrintSlice"(%"github.com/goplus/llgo/internal/runtime.Slice" %196) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintSlice"(%"github.com/goplus/llgo/internal/runtime.Slice" %201) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %202 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringFromBytes"(%"github.com/goplus/llgo/internal/runtime.Slice" %196) + %203 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringFromRunes"(%"github.com/goplus/llgo/internal/runtime.Slice" %201) + %204 = extractvalue %"github.com/goplus/llgo/internal/runtime.Slice" %196, 0 + %205 = getelementptr inbounds i8, ptr %204, i64 3 + %206 = load i8, ptr %205, align 1 + %207 = sext i8 %206 to i32 + %208 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringFromRune"(i32 %207) + %209 = extractvalue %"github.com/goplus/llgo/internal/runtime.Slice" %201, 0 + %210 = getelementptr inbounds i32, ptr %209, i64 0 + %211 = load i32, ptr %210, align 4 + %212 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringFromRune"(i32 %211) + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %202) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %203) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %208) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %212) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %213 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %214 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %213, i32 0, i32 0 + store ptr @9, ptr %214, align 8 + %215 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %213, i32 0, i32 1 + store i64 3, ptr %215, align 4 + %216 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %213, align 8 + %217 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %218 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %217, i32 0, i32 0 + store ptr @10, ptr %218, align 8 + %219 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %217, i32 0, i32 1 + store i64 3, ptr %219, align 4 + %220 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %217, align 8 + %221 = call i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String" %216, %"github.com/goplus/llgo/internal/runtime.String" %220) + %222 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %223 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %222, i32 0, i32 0 + store ptr @11, ptr %223, align 8 + %224 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %222, i32 0, i32 1 + store i64 3, ptr %224, align 4 + %225 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %222, align 8 + %226 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %227 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %226, i32 0, i32 0 + store ptr @12, ptr %227, align 8 + %228 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %226, i32 0, i32 1 + store i64 3, ptr %228, align 4 + %229 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %226, align 8 + %230 = call i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String" %225, %"github.com/goplus/llgo/internal/runtime.String" %229) + %231 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %232 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %231, i32 0, i32 0 + store ptr @13, ptr %232, align 8 + %233 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %231, i32 0, i32 1 + store i64 3, ptr %233, align 4 + %234 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %231, align 8 + %235 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %236 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %235, i32 0, i32 0 + store ptr @14, ptr %236, align 8 + %237 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %235, i32 0, i32 1 + store i64 3, ptr %237, align 4 + %238 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %235, align 8 + %239 = call i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String" %234, %"github.com/goplus/llgo/internal/runtime.String" %238) + %240 = xor i1 %239, true + %241 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %242 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %241, i32 0, i32 0 + store ptr @15, ptr %242, align 8 + %243 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %241, i32 0, i32 1 + store i64 3, ptr %243, align 4 + %244 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %241, align 8 + %245 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %246 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %245, i32 0, i32 0 + store ptr @16, ptr %246, align 8 + %247 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %245, i32 0, i32 1 + store i64 3, ptr %247, align 4 + %248 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %245, align 8 + %249 = call i1 @"github.com/goplus/llgo/internal/runtime.StringLess"(%"github.com/goplus/llgo/internal/runtime.String" %244, %"github.com/goplus/llgo/internal/runtime.String" %248) + %250 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %251 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %250, i32 0, i32 0 + store ptr @17, ptr %251, align 8 + %252 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %250, i32 0, i32 1 + store i64 3, ptr %252, align 4 + %253 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %250, align 8 + %254 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %255 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %254, i32 0, i32 0 + store ptr @18, ptr %255, align 8 + %256 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %254, i32 0, i32 1 + store i64 3, ptr %256, align 4 + %257 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %254, align 8 + %258 = call i1 @"github.com/goplus/llgo/internal/runtime.StringLess"(%"github.com/goplus/llgo/internal/runtime.String" %257, %"github.com/goplus/llgo/internal/runtime.String" %253) + %259 = xor i1 %258, true + %260 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %261 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %260, i32 0, i32 0 + store ptr @19, ptr %261, align 8 + %262 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %260, i32 0, i32 1 + store i64 3, ptr %262, align 4 + %263 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %260, align 8 + %264 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %265 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %264, i32 0, i32 0 + store ptr @20, ptr %265, align 8 + %266 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %264, i32 0, i32 1 + store i64 3, ptr %266, align 4 + %267 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %264, align 8 + %268 = call i1 @"github.com/goplus/llgo/internal/runtime.StringLess"(%"github.com/goplus/llgo/internal/runtime.String" %267, %"github.com/goplus/llgo/internal/runtime.String" %263) + %269 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %270 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %269, i32 0, i32 0 + store ptr @21, ptr %270, align 8 + %271 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %269, i32 0, i32 1 + store i64 3, ptr %271, align 4 + %272 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %269, align 8 + %273 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %274 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %273, i32 0, i32 0 + store ptr @22, ptr %274, align 8 + %275 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %273, i32 0, i32 1 + store i64 3, ptr %275, align 4 + %276 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %273, align 8 + %277 = call i1 @"github.com/goplus/llgo/internal/runtime.StringLess"(%"github.com/goplus/llgo/internal/runtime.String" %272, %"github.com/goplus/llgo/internal/runtime.String" %276) + %278 = xor i1 %277, true + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %221) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %230) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %240) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %249) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %259) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %268) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintBool"(i1 %278) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) ret i32 0 } -declare void @"github.com/goplus/llgo/internal/runtime.init"() - declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) +declare void @"github.com/goplus/llgo/internal/runtime.init"() + declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr, i64, i64, i64, i64, i64) declare void @"github.com/goplus/llgo/internal/runtime.PrintSlice"(%"github.com/goplus/llgo/internal/runtime.Slice") @@ -431,7 +671,7 @@ define void @"main.main$1"() { _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 - store ptr @6, ptr %1, align 8 + store ptr @23, ptr %1, align 8 %2 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %0, i32 0, i32 1 store i64 2, ptr %2, align 4 %3 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %0, align 8 @@ -439,3 +679,21 @@ _llgo_0: call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) ret void } + +declare ptr @"github.com/goplus/llgo/internal/runtime.NewStringIter"(%"github.com/goplus/llgo/internal/runtime.String") + +declare { i1, i64, i32 } @"github.com/goplus/llgo/internal/runtime.StringIterNext"(ptr) + +declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.StringToBytes"(%"github.com/goplus/llgo/internal/runtime.String") + +declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.StringToRunes"(%"github.com/goplus/llgo/internal/runtime.String") + +declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringFromBytes"(%"github.com/goplus/llgo/internal/runtime.Slice") + +declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringFromRunes"(%"github.com/goplus/llgo/internal/runtime.Slice") + +declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringFromRune"(i32) + +declare i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String", %"github.com/goplus/llgo/internal/runtime.String") + +declare i1 @"github.com/goplus/llgo/internal/runtime.StringLess"(%"github.com/goplus/llgo/internal/runtime.String", %"github.com/goplus/llgo/internal/runtime.String") diff --git a/cl/_testrt/cast/out.ll b/cl/_testrt/cast/out.ll index 794eea9e..1f82dabd 100644 --- a/cl/_testrt/cast/out.ll +++ b/cl/_testrt/cast/out.ll @@ -82,7 +82,7 @@ _llgo_2: ; preds = %_llgo_0 define void @main.cvt32Fto64F(float %0, double %1) { _llgo_0: %2 = fpext float %0 to double - %3 = fcmp one double %2, %1 + %3 = fcmp une double %2, %1 br i1 %3, label %_llgo_1, label %_llgo_2 _llgo_1: ; preds = %_llgo_0 @@ -198,7 +198,7 @@ _llgo_2: ; preds = %_llgo_0 define void @main.cvt64Fto32F(double %0, float %1) { _llgo_0: %2 = fptrunc double %0 to float - %3 = fcmp one float %2, %1 + %3 = fcmp une float %2, %1 br i1 %3, label %_llgo_1, label %_llgo_2 _llgo_1: ; preds = %_llgo_0 @@ -227,7 +227,7 @@ _llgo_2: ; preds = %_llgo_0 define void @main.cvt64Uto64F(i64 %0, double %1) { _llgo_0: %2 = uitofp i64 %0 to double - %3 = fcmp one double %2, %1 + %3 = fcmp une double %2, %1 br i1 %3, label %_llgo_1, label %_llgo_2 _llgo_1: ; preds = %_llgo_0 @@ -256,7 +256,7 @@ _llgo_2: ; preds = %_llgo_0 define void @main.cvt64to64F(i64 %0, double %1) { _llgo_0: %2 = sitofp i64 %0 to double - %3 = fcmp one double %2, %1 + %3 = fcmp une double %2, %1 br i1 %3, label %_llgo_1, label %_llgo_2 _llgo_1: ; preds = %_llgo_0 diff --git a/cl/compile.go b/cl/compile.go index 1b23d3b8..991c136e 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -714,6 +714,12 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue case *ssa.Extract: x := p.compileValue(b, v.Tuple) ret = b.Extract(x, v.Index) + case *ssa.Range: + x := p.compileValue(b, v.X) + ret = b.Range(x) + case *ssa.Next: + iter := p.compileValue(b, v.Iter) + ret = b.Next(iter, v.IsString) default: panic(fmt.Sprintf("compileInstrAndValue: unknown instr - %T\n", iv)) } diff --git a/internal/runtime/llgo_autogen.lla b/internal/runtime/llgo_autogen.lla index a5c5b75b..92d400d6 100644 Binary files a/internal/runtime/llgo_autogen.lla and b/internal/runtime/llgo_autogen.lla differ diff --git a/internal/runtime/utf8.go b/internal/runtime/utf8.go new file mode 100644 index 00000000..f43ee67e --- /dev/null +++ b/internal/runtime/utf8.go @@ -0,0 +1,132 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +// Numbers fundamental to the encoding. +const ( + runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character" + runeSelf = 0x80 // characters below runeSelf are represented as themselves in a single byte. + maxRune = '\U0010FFFF' // Maximum valid Unicode code point. +) + +// Code points in the surrogate range are not valid for UTF-8. +const ( + surrogateMin = 0xD800 + surrogateMax = 0xDFFF +) + +const ( + t1 = 0x00 // 0000 0000 + tx = 0x80 // 1000 0000 + t2 = 0xC0 // 1100 0000 + t3 = 0xE0 // 1110 0000 + t4 = 0xF0 // 1111 0000 + t5 = 0xF8 // 1111 1000 + + maskx = 0x3F // 0011 1111 + mask2 = 0x1F // 0001 1111 + mask3 = 0x0F // 0000 1111 + mask4 = 0x07 // 0000 0111 + + rune1Max = 1<<7 - 1 + rune2Max = 1<<11 - 1 + rune3Max = 1<<16 - 1 + + // The default lowest and highest continuation byte. + locb = 0x80 // 1000 0000 + hicb = 0xBF // 1011 1111 +) + +// countrunes returns the number of runes in s. +// func countrunes(s string) int { +// n := 0 +// for range s { +// n++ +// } +// return n +// } + +// decoderune returns the non-ASCII rune at the start of +// s[k:] and the index after the rune in s. +// +// decoderune assumes that caller has checked that +// the to be decoded rune is a non-ASCII rune. +// +// If the string appears to be incomplete or decoding problems +// are encountered (runeerror, k + 1) is returned to ensure +// progress when decoderune is used to iterate over a string. +func decoderune(s string, k int) (r rune, pos int) { + pos = k + + if k >= len(s) { + return runeError, k + 1 + } + + s = s[k:] + + switch { + case t2 <= s[0] && s[0] < t3: + // 0080-07FF two byte sequence + if len(s) > 1 && (locb <= s[1] && s[1] <= hicb) { + r = rune(s[0]&mask2)<<6 | rune(s[1]&maskx) + pos += 2 + if rune1Max < r { + return + } + } + case t3 <= s[0] && s[0] < t4: + // 0800-FFFF three byte sequence + if len(s) > 2 && (locb <= s[1] && s[1] <= hicb) && (locb <= s[2] && s[2] <= hicb) { + r = rune(s[0]&mask3)<<12 | rune(s[1]&maskx)<<6 | rune(s[2]&maskx) + pos += 3 + if rune2Max < r && !(surrogateMin <= r && r <= surrogateMax) { + return + } + } + case t4 <= s[0] && s[0] < t5: + // 10000-1FFFFF four byte sequence + if len(s) > 3 && (locb <= s[1] && s[1] <= hicb) && (locb <= s[2] && s[2] <= hicb) && (locb <= s[3] && s[3] <= hicb) { + r = rune(s[0]&mask4)<<18 | rune(s[1]&maskx)<<12 | rune(s[2]&maskx)<<6 | rune(s[3]&maskx) + pos += 4 + if rune3Max < r && r <= maxRune { + return + } + } + } + + return runeError, k + 1 +} + +// encoderune writes into p (which must be large enough) the UTF-8 encoding of the rune. +// It returns the number of bytes written. +func encoderune(p []byte, r rune) int { + // Negative values are erroneous. Making it unsigned addresses the problem. + switch i := uint32(r); { + case i <= rune1Max: + p[0] = byte(r) + return 1 + case i <= rune2Max: + _ = p[1] // eliminate bounds checks + p[0] = t2 | byte(r>>6) + p[1] = tx | byte(r)&maskx + return 2 + case i > maxRune, surrogateMin <= i && i <= surrogateMax: + r = runeError + fallthrough + case i <= rune3Max: + _ = p[2] // eliminate bounds checks + p[0] = t3 | byte(r>>12) + p[1] = tx | byte(r>>6)&maskx + p[2] = tx | byte(r)&maskx + return 3 + default: + _ = p[3] // eliminate bounds checks + p[0] = t4 | byte(r>>18) + p[1] = tx | byte(r>>12)&maskx + p[2] = tx | byte(r>>6)&maskx + p[3] = tx | byte(r)&maskx + return 4 + } +} diff --git a/internal/runtime/z_string.go b/internal/runtime/z_string.go index f4d20906..e34ae730 100644 --- a/internal/runtime/z_string.go +++ b/internal/runtime/z_string.go @@ -70,4 +70,119 @@ func NewStringSlice(base String, i, j int) String { return String{nil, 0} } +type StringIter struct { + s string + pos int +} + +func NewStringIter(s string) *StringIter { + return &StringIter{s, 0} +} + +func StringIterNext(it *StringIter) (ok bool, k int, v rune) { + if it.pos >= len(it.s) { + return false, 0, 0 + } + k = it.pos + if c := it.s[it.pos]; c < runeSelf { + it.pos++ + v = rune(c) + } else { + v, it.pos = decoderune(it.s, it.pos) + } + ok = true + return +} + +func StringToBytes(s String) []byte { + if s.len == 0 { + return nil + } + data := make([]byte, s.len) + c.Memcpy(unsafe.Pointer(&data[0]), s.data, uintptr(s.len)) + return data +} + +func StringToRunes(s string) []rune { + if len(s) == 0 { + return nil + } + data := make([]rune, len(s)) + var index uint + for i := 0; i < len(s); { + if c := s[i]; c < runeSelf { + data[index] = rune(c) + i++ + } else { + data[index], i = decoderune(s, i) + } + index++ + } + return data[:index:index] +} + +func StringFromBytes(b Slice) (s String) { + if b.len == 0 { + return + } + s.len = b.len + s.data = AllocU(uintptr(s.len)) + c.Memcpy(s.data, b.data, uintptr(b.len)) + return +} + +func StringFromRunes(rs []rune) (s String) { + if len(rs) == 0 { + return + } + data := make([]byte, len(rs)*4) + var index int + for _, r := range rs { + n := encoderune(data[index:], r) + index += n + } + s.len = index + s.data = unsafe.Pointer(&data[0]) + return +} + +func StringFromRune(r rune) (s String) { + var buf [4]byte + n := encoderune(buf[:], r) + s.len = n + s.data = unsafe.Pointer(&buf[0]) + return +} + +func StringEqual(x, y String) bool { + if x.len != y.len { + return false + } + if x.data != y.data { + for i := 0; i < x.len; i++ { + if *(*byte)(c.Advance(x.data, i)) != *(*byte)(c.Advance(y.data, i)) { + return false + } + } + } + return true +} + +func StringLess(x, y String) bool { + n := x.len + if n > y.len { + n = y.len + } + for i := 0; i < n; i++ { + ix := *(*byte)(c.Advance(x.data, i)) + iy := *(*byte)(c.Advance(y.data, i)) + if ix < iy { + return true + } else if ix > iy { + return false + } + } + return x.len < y.len +} + // ----------------------------------------------------------------------------- diff --git a/ssa/expr.go b/ssa/expr.go index 314848a4..0258755c 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -315,7 +315,7 @@ var uintPredOpToLLVM = []llvm.IntPredicate{ var floatPredOpToLLVM = []llvm.FloatPredicate{ token.EQL - predOpBase: llvm.FloatOEQ, - token.NEQ - predOpBase: llvm.FloatONE, + token.NEQ - predOpBase: llvm.FloatUNE, token.LSS - predOpBase: llvm.FloatOLT, token.LEQ - predOpBase: llvm.FloatOLE, token.GTR - predOpBase: llvm.FloatOGT, @@ -405,7 +405,26 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr { pred := boolPredOpToLLVM[op-predOpBase] return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret} case vkString, vkComplex: - panic("todo") + switch op { + case token.EQL: + return b.InlineCall(b.Pkg.rtFunc("StringEqual"), x, y) + case token.NEQ: + ret := b.InlineCall(b.Pkg.rtFunc("StringEqual"), x, y) + ret.impl = llvm.CreateNot(b.impl, ret.impl) + return ret + case token.LSS: + return b.InlineCall(b.Pkg.rtFunc("StringLess"), x, y) + case token.LEQ: + ret := b.InlineCall(b.Pkg.rtFunc("StringLess"), y, x) + ret.impl = llvm.CreateNot(b.impl, ret.impl) + return ret + case token.GTR: + return b.InlineCall(b.Pkg.rtFunc("StringLess"), y, x) + case token.GEQ: + ret := b.InlineCall(b.Pkg.rtFunc("StringLess"), x, y) + ret.impl = llvm.CreateNot(b.impl, ret.impl) + return ret + } } } panic("todo") @@ -833,6 +852,27 @@ func (b Builder) Convert(t Type, x Expr) (ret Expr) { case types.UnsafePointer: ret.impl = castPtr(b.impl, x.impl, t.ll) return + case types.String: + switch xtyp := x.RawType().Underlying().(type) { + case *types.Slice: + if etyp, ok := xtyp.Elem().Underlying().(*types.Basic); ok { + switch etyp.Kind() { + case types.Byte: + ret.impl = b.InlineCall(b.Func.Pkg.rtFunc("StringFromBytes"), x).impl + return + case types.Rune: + ret.impl = b.InlineCall(b.Func.Pkg.rtFunc("StringFromRunes"), x).impl + return + } + } + case *types.Basic: + if x.Type != b.Prog.Int32() { + x.Type = b.Prog.Int32() + x.impl = castInt(b, x.impl, b.Prog.Int32()) + } + ret.impl = b.InlineCall(b.Func.Pkg.rtFunc("StringFromRune"), x).impl + return + } } switch xtyp := x.RawType().Underlying().(type) { case *types.Basic: @@ -867,6 +907,19 @@ func (b Builder) Convert(t Type, x Expr) (ret Expr) { case *types.Pointer: ret.impl = castPtr(b.impl, x.impl, t.ll) return + case *types.Slice: + if x.kind == vkString { + if etyp, ok := typ.Elem().Underlying().(*types.Basic); ok { + switch etyp.Kind() { + case types.Byte: + ret.impl = b.InlineCall(b.Func.Pkg.rtFunc("StringToBytes"), x).impl + return + case types.Rune: + ret.impl = b.InlineCall(b.Func.Pkg.rtFunc("StringToRunes"), x).impl + return + } + } + } } panic("todo") } @@ -969,6 +1022,51 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { return } +// The Range instruction yields an iterator over the domain and range +// of X, which must be a string or map. +// +// Elements are accessed via Next. +// +// Type() returns an opaque and degenerate "rangeIter" type. +// +// Pos() returns the ast.RangeStmt.For. +// +// Example printed form: +// +// t0 = range "hello":string +func (b Builder) Range(x Expr) Expr { + switch x.kind { + case vkString: + return b.InlineCall(b.Pkg.rtFunc("NewStringIter"), x) + } + panic("todo") +} + +// The Next instruction reads and advances the (map or string) +// iterator Iter and returns a 3-tuple value (ok, k, v). If the +// iterator is not exhausted, ok is true and k and v are the next +// elements of the domain and range, respectively. Otherwise ok is +// false and k and v are undefined. +// +// Components of the tuple are accessed using Extract. +// +// The IsString field distinguishes iterators over strings from those +// over maps, as the Type() alone is insufficient: consider +// map[int]rune. +// +// Type() returns a *types.Tuple for the triple (ok, k, v). +// The types of k and/or v may be types.Invalid. +// +// Example printed form: +// +// t1 = next t0 +func (b Builder) Next(iter Expr, isString bool) (ret Expr) { + if isString { + return b.InlineCall(b.Pkg.rtFunc("StringIterNext"), iter) + } + panic("todo") +} + // A Builtin represents a specific use of a built-in function, e.g. len. // // Builtins are immutable values. Builtins do not have addresses.