diff --git a/cl/_testrt/concat/in.go b/cl/_testrt/concat/in.go index 5328026a..811a0c8d 100644 --- a/cl/_testrt/concat/in.go +++ b/cl/_testrt/concat/in.go @@ -4,6 +4,14 @@ import ( "github.com/goplus/llgo/internal/runtime/c" ) -func main() { - c.Fprintf(c.Stderr, c.Str("Hello %d\n"), 100) +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("Hello %s\n"), c.AllocaCStr(result)) } diff --git a/cl/_testrt/concat/out.ll b/cl/_testrt/concat/out.ll index ee0cd9d7..06e49967 100644 --- a/cl/_testrt/concat/out.ll +++ b/cl/_testrt/concat/out.ll @@ -1,9 +1,40 @@ ; 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 -@0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1 +@4 = private unnamed_addr constant [10 x i8] c"Hello %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) + br label %_llgo_1 + +_llgo_1: ; preds = %_llgo_2, %_llgo_0 + %2 = phi %"github.com/goplus/llgo/internal/runtime.String" [ %10, %_llgo_0 ], [ %9, %_llgo_2 ] + %3 = phi i64 [ -1, %_llgo_0 ], [ %4, %_llgo_2 ] + %4 = add i64 %3, 1 + %5 = icmp slt i64 %4, %1 + br i1 %5, label %_llgo_2, label %_llgo_3 + +_llgo_2: ; preds = %_llgo_1 + %6 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) + %7 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %6, i64 %4 + %8 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %7, align 8 + %9 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.String" %2, %"github.com/goplus/llgo/internal/runtime.String" %8) + br label %_llgo_1 + +_llgo_3: ; preds = %_llgo_1 + ret %"github.com/goplus/llgo/internal/runtime.String" %2 + %10 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 0) +} define void @main.init() { _llgo_0: @@ -22,11 +53,43 @@ 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 - %1 = call i32 (ptr, ptr, ...) @fprintf(ptr %0, ptr @0, i64 100) + %0 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(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.NewSlice"(ptr %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.Slice") + +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.Alloc"(i64) + +declare %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice"(ptr, i64, i64) + +declare i64 @"github.com/goplus/llgo/internal/runtime.StringLen"(%"github.com/goplus/llgo/internal/runtime.Slice") + +declare ptr @"github.com/goplus/llgo/internal/runtime.CStrCopy"(ptr, %"github.com/goplus/llgo/internal/runtime.String") + declare i32 @fprintf(ptr, ptr, ...) diff --git a/internal/runtime/llgo_autogen.ll b/internal/runtime/llgo_autogen.ll index a51d9cbe..7c522d8e 100644 --- a/internal/runtime/llgo_autogen.ll +++ b/internal/runtime/llgo_autogen.ll @@ -13,7 +13,8 @@ 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 [5 x i8] c"todo\00", align 1 +@2 = private unnamed_addr constant [11 x i8] c"panic: %s\0A\00", align 1 define ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 %0) { _llgo_0: @@ -265,6 +266,37 @@ _llgo_0: ret i64 %3 } +define %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.StringCat"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) { +_llgo_0: + %1 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %2 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) + br label %_llgo_1 + +_llgo_1: ; preds = %_llgo_2, %_llgo_0 + %3 = phi i64 [ 0, %_llgo_0 ], [ %12, %_llgo_2 ] + %4 = phi i64 [ -1, %_llgo_0 ], [ %5, %_llgo_2 ] + %5 = add i64 %4, 1 + %6 = icmp slt i64 %5, %2 + br i1 %6, label %_llgo_2, label %_llgo_3 + +_llgo_2: ; preds = %_llgo_1 + %7 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) + %8 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %7, i64 %5 + %9 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %8, align 8 + store %"github.com/goplus/llgo/internal/runtime.String" %9, ptr %1, align 8 + %10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %1, i32 0, i32 1 + %11 = load i64, ptr %10, align 4 + %12 = add i64 %3, %11 + br label %_llgo_1 + +_llgo_3: ; preds = %_llgo_1 + %13 = call ptr @"github.com/goplus/llgo/internal/runtime.Alloc"(i64 %3) + %14 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @1, i64 4) + %15 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyString"(%"github.com/goplus/llgo/internal/runtime.String" %14) + call void @"github.com/goplus/llgo/internal/runtime.TracePanic"(%"github.com/goplus/llgo/internal/runtime.iface" %15) + unreachable +} + 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 @@ -309,7 +341,7 @@ _llgo_2: ; preds = %_llgo_0 %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) + %18 = call i32 (ptr, ...) @printf(ptr @2, ptr %17) br label %_llgo_1 } diff --git a/internal/runtime/z_string.go b/internal/runtime/z_string.go index 1ee8109a..a514816a 100644 --- a/internal/runtime/z_string.go +++ b/internal/runtime/z_string.go @@ -55,6 +55,17 @@ func StringData(s String) unsafe.Pointer { return s.data } +// StringCat concatenates strings. +func StringCat(args ...String) String { + n := 0 + for _, v := range args { + n += v.len + } + ret := Alloc(uintptr(n)) + _ = ret + panic("todo") +} + // ----------------------------------------------------------------------------- // CStrCopy copies a Go string to a C string buffer and returns it. diff --git a/ssa/expr.go b/ssa/expr.go index c19ee41d..1df81897 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -268,12 +268,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 {