diff --git a/cl/_testdata/vargs/in.go b/cl/_testdata/vargs/in.go new file mode 100644 index 00000000..417911cb --- /dev/null +++ b/cl/_testdata/vargs/in.go @@ -0,0 +1,13 @@ +package main + +import "github.com/goplus/llgo/internal/runtime/c" + +func test(a ...any) { + for _, v := range a { + c.Printf(c.Str("%d\n"), v.(int)) + } +} + +func main() { + test(1, 2, 3) +} diff --git a/cl/_testdata/vargs/out.ll b/cl/_testdata/vargs/out.ll new file mode 100644 index 00000000..56930b8e --- /dev/null +++ b/cl/_testdata/vargs/out.ll @@ -0,0 +1,89 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr } +%"github.com/goplus/llgo/internal/runtime.Slice" = type { ptr, i64, i64 } + +@"main.init$guard" = global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = 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(i32 %0, ptr %1) { +_llgo_0: + store i32 %0, ptr @__llgo_argc, align 4 + store ptr %1, ptr @__llgo_argv, align 8 + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 48) + %3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i64 0 + %4 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2) + %5 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %4, i64 1) + store %"github.com/goplus/llgo/internal/runtime.iface" %5, ptr %3, align 8 + %6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i64 1 + %7 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2) + %8 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %7, i64 2) + store %"github.com/goplus/llgo/internal/runtime.iface" %8, ptr %6, align 8 + %9 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %2, i64 2 + %10 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2) + %11 = call %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr %10, i64 3) + store %"github.com/goplus/llgo/internal/runtime.iface" %11, ptr %9, align 8 + %12 = call %"github.com/goplus/llgo/internal/runtime.Slice" @"github.com/goplus/llgo/internal/runtime.NewSlice3"(ptr %2, i64 16, i64 3, i64 0, i64 3, i64 3) + call void @main.test(%"github.com/goplus/llgo/internal/runtime.Slice" %12) + ret void +} + +define void @main.test(%"github.com/goplus/llgo/internal/runtime.Slice" %0) { +_llgo_0: + %1 = call i64 @"github.com/goplus/llgo/internal/runtime.SliceLen"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) + br label %_llgo_1 + +_llgo_1: ; preds = %_llgo_2, %_llgo_0 + %2 = phi i64 [ -1, %_llgo_0 ], [ %3, %_llgo_2 ] + %3 = add i64 %2, 1 + %4 = icmp slt i64 %3, %1 + br i1 %4, label %_llgo_2, label %_llgo_3 + +_llgo_2: ; preds = %_llgo_1 + %5 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %0) + %6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.iface", ptr %5, i64 %3 + %7 = load %"github.com/goplus/llgo/internal/runtime.iface", ptr %6, align 8 + %8 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 2) + %9 = call i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface" %7, ptr %8) + %10 = call i32 (ptr, ...) @printf(ptr @0, i64 %9) + br label %_llgo_1 + +_llgo_3: ; preds = %_llgo_1 + ret void +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) + +declare ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64) + +declare %"github.com/goplus/llgo/internal/runtime.iface" @"github.com/goplus/llgo/internal/runtime.MakeAnyInt"(ptr, 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 ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice") + +declare i64 @"github.com/goplus/llgo/internal/runtime.I2Int"(%"github.com/goplus/llgo/internal/runtime.iface", ptr) + +declare i32 @printf(ptr, ...) diff --git a/cl/compile.go b/cl/compile.go index 09770370..354f27ed 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -596,12 +596,18 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue } ret = b.Slice(x, low, high, max) case *ssa.MakeInterface: - const ( - delayExpr = true // varargs: don't need to convert an expr to any - ) + if refs := *v.Referrers(); len(refs) == 1 { + if ref, ok := refs[0].(*ssa.Store); ok { + if va, ok := ref.Addr.(*ssa.IndexAddr); ok { + if _, ok = p.isVArgs(va.X); ok { // varargs: this is a varargs store + return + } + } + } + } t := p.prog.Type(v.Type(), llssa.InGo) x := p.compileValue(b, v.X) - ret = b.MakeInterface(t, x, delayExpr) + ret = b.MakeInterface(t, x) case *ssa.MakeSlice: var nCap llssa.Expr t := p.prog.Type(v.Type(), llssa.InGo) diff --git a/cl/compile_test.go b/cl/compile_test.go index bf0f8fc2..76be7e7a 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -37,7 +37,7 @@ func TestFromTestrt(t *testing.T) { } func TestFromTestdata(t *testing.T) { - cltest.FromDir(t, "vargs", "./_testdata", false) + cltest.FromDir(t, "", "./_testdata", false) } func TestSqlite(t *testing.T) { diff --git a/ssa/expr.go b/ssa/expr.go index 5a6bbb57..8b254a75 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -44,8 +44,6 @@ func (v Expr) IsNil() bool { // Do evaluates the delay expression and returns the result. func (v Expr) Do(b Builder) Expr { switch vt := v.Type; vt.kind { - case vkDelayExpr: - return vt.raw.Type.(delayExprTy)() case vkPhisExpr: e := vt.raw.Type.(*phisExprTy) return b.aggregateValue(e.Type, e.phis...) @@ -55,23 +53,6 @@ func (v Expr) Do(b Builder) Expr { // ----------------------------------------------------------------------------- -// DelayExpr returns a delay expression. -func DelayExpr(f func() Expr) Expr { - return Expr{Type: &aType{raw: rawType{delayExprTy(f)}, kind: vkDelayExpr}} -} - -type delayExprTy func() Expr - -func (p delayExprTy) Underlying() types.Type { - panic("don't call") -} - -func (p delayExprTy) String() string { - return "delayExpr" -} - -// ----------------------------------------------------------------------------- - type phisExprTy struct { phis []llvm.Value Type @@ -1059,35 +1040,27 @@ func castPtr(b llvm.Builder, x llvm.Value, t llvm.Type) llvm.Value { // // t1 = make interface{} <- int (42:int) // t2 = make Stringer <- t0 -func (b Builder) MakeInterface(tinter Type, x Expr, mayDelay bool) (ret Expr) { +func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { raw := tinter.raw.Type if debugInstr { log.Printf("MakeInterface %v, %v\n", raw, x.impl) } - tiund := raw.Underlying().(*types.Interface) - isAny := tiund.Empty() - fnDo := func() Expr { - prog := b.Prog - pkg := b.Func.Pkg - switch tx := x.raw.Type.Underlying().(type) { - case *types.Basic: - kind := tx.Kind() - switch { - case kind >= types.Int && kind <= types.Uintptr: - t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind))) - tptr := prog.Uintptr() - vptr := Expr{llvm.CreateIntCast(b.impl, x.impl, tptr.ll), tptr} - return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter} - case kind == types.String: - return Expr{b.InlineCall(pkg.rtFunc("MakeAnyString"), x).impl, tinter} - } + prog := b.Prog + pkg := b.Func.Pkg + switch tx := x.raw.Type.Underlying().(type) { + case *types.Basic: + kind := tx.Kind() + switch { + case kind >= types.Int && kind <= types.Uintptr: + t := b.InlineCall(pkg.rtFunc("Basic"), prog.Val(int(kind))) + tptr := prog.Uintptr() + vptr := Expr{llvm.CreateIntCast(b.impl, x.impl, tptr.ll), tptr} + return Expr{b.InlineCall(pkg.rtFunc("MakeAnyInt"), t, vptr).impl, tinter} + case kind == types.String: + return Expr{b.InlineCall(pkg.rtFunc("MakeAnyString"), x).impl, tinter} } - panic("todo") } - if mayDelay && isAny { - return DelayExpr(fnDo) - } - return fnDo() + panic("todo") } // The TypeAssert instruction tests whether interface value X has type diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 6253d21d..cc9d8621 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -73,9 +73,7 @@ func TestCvtType(t *testing.T) { } func TestUserdefExpr(t *testing.T) { - a := delayExprTy(nil) b := &phisExprTy{} - _ = a.String() _ = b.String() test := func(a types.Type) { defer func() { @@ -85,7 +83,6 @@ func TestUserdefExpr(t *testing.T) { }() a.Underlying() } - test(a) test(b) } diff --git a/ssa/type.go b/ssa/type.go index a71f878f..aecad47d 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -44,8 +44,7 @@ const ( vkFuncPtr vkClosure vkTuple - vkDelayExpr = -1 - vkPhisExpr = -2 + vkPhisExpr = -1 ) // -----------------------------------------------------------------------------