diff --git a/cl/_testgo/closure/out.ll b/cl/_testgo/closure/out.ll index b387030b..652eefd1 100644 --- a/cl/_testgo/closure/out.ll +++ b/cl/_testgo/closure/out.ll @@ -2,6 +2,7 @@ source_filename = "main" %"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } +%main.T = type { ptr, ptr } @"main.init$guard" = global i1 false, align 1 @__llgo_argc = global i32 0, align 4 @@ -37,12 +38,12 @@ _llgo_0: store i64 3, ptr %5, align 4 %6 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %3, align 8 store %"github.com/goplus/llgo/internal/runtime.String" %6, ptr %2, align 8 - %7 = alloca { ptr, ptr }, align 8 - %8 = getelementptr inbounds { ptr, ptr }, ptr %7, i32 0, i32 0 + %7 = alloca %main.T, align 8 + %8 = getelementptr inbounds %main.T, ptr %7, i32 0, i32 0 store ptr @"__llgo_stub.main.main$1", ptr %8, align 8 - %9 = getelementptr inbounds { ptr, ptr }, ptr %7, i32 0, i32 1 + %9 = getelementptr inbounds %main.T, ptr %7, i32 0, i32 1 store ptr null, ptr %9, align 8 - %10 = load { ptr, ptr }, ptr %7, align 8 + %10 = load %main.T, ptr %7, align 8 %11 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) %12 = getelementptr inbounds { ptr }, ptr %11, i32 0, i32 0 store ptr %2, ptr %12, align 8 @@ -52,12 +53,15 @@ _llgo_0: %15 = getelementptr inbounds { ptr, ptr }, ptr %13, i32 0, i32 1 store ptr %11, ptr %15, align 8 %16 = load { ptr, ptr }, ptr %13, align 8 - %17 = extractvalue { ptr, ptr } %10, 1 - %18 = extractvalue { ptr, ptr } %10, 0 - call void %18(ptr %17, i64 100) - %19 = extractvalue { ptr, ptr } %16, 1 - %20 = extractvalue { ptr, ptr } %16, 0 - call void %20(ptr %19, i64 200) + %17 = alloca %main.T, align 8 + store { ptr, ptr } %16, ptr %17, align 8 + %18 = load %main.T, ptr %17, align 8 + %19 = extractvalue %main.T %10, 1 + %20 = extractvalue %main.T %10, 0 + call void %20(ptr %19, i64 100) + %21 = extractvalue %main.T %18, 1 + %22 = extractvalue %main.T %18, 0 + call void %22(ptr %21, i64 200) ret i32 0 } diff --git a/cl/_testgo/invoke/out.ll b/cl/_testgo/invoke/out.ll index 03a2ae86..b1222dc0 100644 --- a/cl/_testgo/invoke/out.ll +++ b/cl/_testgo/invoke/out.ll @@ -304,13 +304,13 @@ _llgo_0: %14 = getelementptr inbounds %main.T5, ptr %13, i32 0, i32 0 store i64 300, ptr %14, align 4 %15 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 16) - %16 = alloca { ptr, ptr }, align 8 - %17 = getelementptr inbounds { ptr, ptr }, ptr %16, i32 0, i32 0 + %16 = alloca %main.T6, align 8 + %17 = getelementptr inbounds %main.T6, ptr %16, i32 0, i32 0 store ptr @"__llgo_stub.main.main$1", ptr %17, align 8 - %18 = getelementptr inbounds { ptr, ptr }, ptr %16, i32 0, i32 1 + %18 = getelementptr inbounds %main.T6, ptr %16, i32 0, i32 1 store ptr null, ptr %18, align 8 - %19 = load { ptr, ptr }, ptr %16, align 8 - store { ptr, ptr } %19, ptr %15, align 8 + %19 = load %main.T6, ptr %16, align 8 + store %main.T6 %19, ptr %15, align 8 %20 = load %main.T, ptr %2, align 8 %21 = load ptr, ptr @_llgo_main.T, align 8 %22 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) diff --git a/cl/_testrt/closureconv/in.go b/cl/_testrt/closureconv/in.go new file mode 100644 index 00000000..0b80ae37 --- /dev/null +++ b/cl/_testrt/closureconv/in.go @@ -0,0 +1,63 @@ +package main + +type Func func(a int, b int) int +type Func2 func(a int, b int) int + +type Call struct { + fn Func + n int +} + +func (c *Call) add(a int, b int) int { + return a + b + c.n +} + +func add(a int, b int) int { + return a + b +} + +func demo1(n int) Func { + m := &Call{n: n} + m.fn = m.add + return m.fn +} + +func demo2() Func { + m := &Call{} + return m.add +} + +func demo3() Func { + return add +} + +func demo4() Func { + return func(a, b int) int { return a + b } +} + +func demo5(n int) Func { + return func(a, b int) int { return a + b + n } +} + +func main() { + n1 := demo1(1)(99, 200) + println(n1) + + n2 := demo2()(100, 200) + println(n2) + + n3 := demo3()(100, 200) + println(n3) + + n4 := demo4()(100, 200) + println(n4) + + n5 := demo5(1)(99, 200) + println(n5) + + var fn func(a int, b int) int = demo5(1) + println(fn(99, 200)) + + var fn2 Func2 = (Func2)(demo5(1)) + println(fn2(99, 200)) +} diff --git a/cl/_testrt/closureconv/out.ll b/cl/_testrt/closureconv/out.ll new file mode 100644 index 00000000..f69440d5 --- /dev/null +++ b/cl/_testrt/closureconv/out.ll @@ -0,0 +1,220 @@ +; ModuleID = 'main' +source_filename = "main" + +%main.Call = type { %main.Func, i64 } +%main.Func = type { ptr, ptr } + +@"main.init$guard" = global i1 false, align 1 +@__llgo_argc = global i32 0, align 4 +@__llgo_argv = global ptr null, align 8 + +define i64 @"main.(*Call).add"(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = add i64 %1, %2 + %4 = getelementptr inbounds %main.Call, ptr %0, i32 0, i32 1 + %5 = load i64, ptr %4, align 4 + %6 = add i64 %3, %5 + ret i64 %6 +} + +define i64 @main.add(i64 %0, i64 %1) { +_llgo_0: + %2 = add i64 %0, %1 + ret i64 %2 +} + +define %main.Func @main.demo1(i64 %0) { +_llgo_0: + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 24) + %2 = getelementptr inbounds %main.Call, ptr %1, i32 0, i32 1 + store i64 %0, ptr %2, align 4 + %3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) + %4 = getelementptr inbounds { ptr }, ptr %3, i32 0, i32 0 + store ptr %1, ptr %4, align 8 + %5 = alloca { ptr, ptr }, align 8 + %6 = getelementptr inbounds { ptr, ptr }, ptr %5, i32 0, i32 0 + store ptr @"main.add$bound", ptr %6, align 8 + %7 = getelementptr inbounds { ptr, ptr }, ptr %5, i32 0, i32 1 + store ptr %3, ptr %7, align 8 + %8 = load { ptr, ptr }, ptr %5, align 8 + %9 = getelementptr inbounds %main.Call, ptr %1, i32 0, i32 0 + %10 = alloca %main.Func, align 8 + store { ptr, ptr } %8, ptr %10, align 8 + %11 = load %main.Func, ptr %10, align 8 + store %main.Func %11, ptr %9, align 8 + %12 = getelementptr inbounds %main.Call, ptr %1, i32 0, i32 0 + %13 = load %main.Func, ptr %12, align 8 + ret %main.Func %13 +} + +define %main.Func @main.demo2() { +_llgo_0: + %0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 24) + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) + %2 = getelementptr inbounds { ptr }, ptr %1, i32 0, i32 0 + store ptr %0, ptr %2, align 8 + %3 = alloca { ptr, ptr }, align 8 + %4 = getelementptr inbounds { ptr, ptr }, ptr %3, i32 0, i32 0 + store ptr @"main.add$bound", ptr %4, align 8 + %5 = getelementptr inbounds { ptr, ptr }, ptr %3, i32 0, i32 1 + store ptr %1, ptr %5, align 8 + %6 = load { ptr, ptr }, ptr %3, align 8 + %7 = alloca %main.Func, align 8 + store { ptr, ptr } %6, ptr %7, align 8 + %8 = load %main.Func, ptr %7, align 8 + ret %main.Func %8 +} + +define %main.Func @main.demo3() { +_llgo_0: + %0 = alloca %main.Func, align 8 + %1 = getelementptr inbounds %main.Func, ptr %0, i32 0, i32 0 + store ptr @__llgo_stub.main.add, ptr %1, align 8 + %2 = getelementptr inbounds %main.Func, ptr %0, i32 0, i32 1 + store ptr null, ptr %2, align 8 + %3 = load %main.Func, ptr %0, align 8 + ret %main.Func %3 +} + +define %main.Func @main.demo4() { +_llgo_0: + %0 = alloca %main.Func, align 8 + %1 = getelementptr inbounds %main.Func, ptr %0, i32 0, i32 0 + store ptr @"__llgo_stub.main.demo4$1", ptr %1, align 8 + %2 = getelementptr inbounds %main.Func, ptr %0, i32 0, i32 1 + store ptr null, ptr %2, align 8 + %3 = load %main.Func, ptr %0, align 8 + ret %main.Func %3 +} + +define i64 @"main.demo4$1"(i64 %0, i64 %1) { +_llgo_0: + %2 = add i64 %0, %1 + ret i64 %2 +} + +define %main.Func @main.demo5(i64 %0) { +_llgo_0: + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) + store i64 %0, ptr %1, align 4 + %2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) + %3 = getelementptr inbounds { ptr }, ptr %2, i32 0, i32 0 + store ptr %1, ptr %3, align 8 + %4 = alloca { ptr, ptr }, align 8 + %5 = getelementptr inbounds { ptr, ptr }, ptr %4, i32 0, i32 0 + store ptr @"main.demo5$1", ptr %5, align 8 + %6 = getelementptr inbounds { ptr, ptr }, ptr %4, i32 0, i32 1 + store ptr %2, ptr %6, align 8 + %7 = load { ptr, ptr }, ptr %4, align 8 + %8 = alloca %main.Func, align 8 + store { ptr, ptr } %7, ptr %8, align 8 + %9 = load %main.Func, ptr %8, align 8 + ret %main.Func %9 +} + +define i64 @"main.demo5$1"(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = add i64 %1, %2 + %4 = load { ptr }, ptr %0, align 8 + %5 = extractvalue { ptr } %4, 0 + %6 = load i64, ptr %5, align 4 + %7 = add i64 %3, %6 + ret i64 %7 +} + +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 i32 @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 %main.Func @main.demo1(i64 1) + %3 = extractvalue %main.Func %2, 1 + %4 = extractvalue %main.Func %2, 0 + %5 = call i64 %4(ptr %3, i64 99, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %5) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %6 = call %main.Func @main.demo2() + %7 = extractvalue %main.Func %6, 1 + %8 = extractvalue %main.Func %6, 0 + %9 = call i64 %8(ptr %7, i64 100, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %9) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %10 = call %main.Func @main.demo3() + %11 = extractvalue %main.Func %10, 1 + %12 = extractvalue %main.Func %10, 0 + %13 = call i64 %12(ptr %11, i64 100, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %13) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %14 = call %main.Func @main.demo4() + %15 = extractvalue %main.Func %14, 1 + %16 = extractvalue %main.Func %14, 0 + %17 = call i64 %16(ptr %15, i64 100, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %17) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %18 = call %main.Func @main.demo5(i64 1) + %19 = extractvalue %main.Func %18, 1 + %20 = extractvalue %main.Func %18, 0 + %21 = call i64 %20(ptr %19, i64 99, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %21) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %22 = call %main.Func @main.demo5(i64 1) + %23 = alloca { ptr, ptr }, align 8 + store %main.Func %22, ptr %23, align 8 + %24 = load { ptr, ptr }, ptr %23, align 8 + %25 = extractvalue { ptr, ptr } %24, 1 + %26 = extractvalue { ptr, ptr } %24, 0 + %27 = call i64 %26(ptr %25, i64 99, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %27) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %28 = call %main.Func @main.demo5(i64 1) + %29 = extractvalue %main.Func %28, 1 + %30 = extractvalue %main.Func %28, 0 + %31 = call i64 %30(ptr %29, i64 99, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %31) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + ret i32 0 +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) + +define i64 @"main.add$bound"(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = load { ptr }, ptr %0, align 8 + %4 = extractvalue { ptr } %3, 0 + %5 = call i64 @"main.(*Call).add"(ptr %4, i64 %1, i64 %2) + ret i64 %5 +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64) + +define linkonce i64 @__llgo_stub.main.add(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = tail call i64 @main.add(i64 %1, i64 %2) + ret i64 %3 +} + +define linkonce i64 @"__llgo_stub.main.demo4$1"(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = tail call i64 @"main.demo4$1"(i64 %1, i64 %2) + ret i64 %3 +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) diff --git a/ssa/expr.go b/ssa/expr.go index 46dd202f..9400958a 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -694,8 +694,30 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { if debugInstr { log.Printf("ChangeType %v, %v\n", t.RawType(), x.impl) } - if t.kind == vkClosure && x.kind == vkFuncDecl { - ret.impl = checkExpr(x, t.raw.Type.Underlying(), b).impl + if t.kind == vkClosure { + switch x.kind { + case vkFuncDecl: + ret.impl = checkExpr(x, t.raw.Type, b).impl + case vkClosure: + convType := func() Expr { + r := Expr{llvm.CreateAlloca(b.impl, t.ll), b.Prog.Pointer(t)} + b.Store(r, x) + return b.Load(r) + } + switch t.RawType().(type) { + case *types.Named: + if _, ok := x.RawType().(*types.Struct); ok { + return convType() + } + case *types.Struct: + if _, ok := x.RawType().(*types.Named); ok { + return convType() + } + } + fallthrough + default: + ret.impl = x.impl + } } else { ret.impl = x.impl } @@ -1172,7 +1194,7 @@ func (b Builder) PrintEx(ln bool, args ...Expr) (ret Expr) { // ----------------------------------------------------------------------------- func checkExpr(v Expr, t types.Type, b Builder) Expr { - if t, ok := t.(*types.Struct); ok && isClosure(t) { + if st, ok := t.Underlying().(*types.Struct); ok && isClosure(st) { if v.kind != vkClosure { return b.Pkg.closureStub(b, t, v) } diff --git a/ssa/package.go b/ssa/package.go index c519cc14..02b0e360 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -608,7 +608,7 @@ const ( closureStub = "__llgo_stub." ) -func (p Package) closureStub(b Builder, t *types.Struct, v Expr) Expr { +func (p Package) closureStub(b Builder, t types.Type, v Expr) Expr { name := v.impl.Name() prog := b.Prog nilVal := prog.Nil(prog.VoidPtr()).impl