diff --git a/cl/_testrt/callback/in.go b/cl/_testrt/callback/in.go new file mode 100644 index 00000000..3e2466f5 --- /dev/null +++ b/cl/_testrt/callback/in.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/goplus/llgo/internal/runtime/c" +) + +func callback(f func()) { + f() +} + +func main() { + callback(func() { + c.Printf(c.Str("Hello, callback\n")) + }) +} diff --git a/cl/_testrt/callback/out.ll b/cl/_testrt/callback/out.ll new file mode 100644 index 00000000..af9f93d3 --- /dev/null +++ b/cl/_testrt/callback/out.ll @@ -0,0 +1,49 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@0 = private unnamed_addr constant [17 x i8] c"Hello, callback\0A\00", align 1 + +define void @main.callback({ ptr, ptr } %0) { +_llgo_0: + %1 = extractvalue { ptr, ptr } %0, 0 + call void %1() + ret void +} + +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 @"main.main$1", 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 + call void @main.callback({ ptr, ptr } %3) + ret void +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +define void @"main.main$1"() { +_llgo_0: + %0 = call i32 (ptr, ...) @printf(ptr @0) + ret void +} + +declare i32 @printf(ptr, ...) diff --git a/cl/_testrt/intgen/out.ll b/cl/_testrt/intgen/out.ll index d3e821e2..8e4e7e60 100644 --- a/cl/_testrt/intgen/out.ll +++ b/cl/_testrt/intgen/out.ll @@ -22,10 +22,10 @@ _llgo_1: ; preds = %_llgo_2, %_llgo_0 _llgo_2: ; preds = %_llgo_1 %9 = extractvalue { ptr, ptr } %1, 0 - %10 = call addrspace(37) { ptr, ptr } %1() + %10 = call i32 %9() %11 = call ptr @"github.com/goplus/llgo/internal/runtime.SliceData"(%"github.com/goplus/llgo/internal/runtime.Slice" %4) %12 = getelementptr inbounds i32, ptr %11, i64 %7 - store ptr %10, ptr %12, align 8 + store i32 %10, ptr %12, align 4 br label %_llgo_1 _llgo_3: ; preds = %_llgo_1 diff --git a/cl/compile_test.go b/cl/compile_test.go index b9c3443f..4420e466 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -29,7 +29,7 @@ func testCompile(t *testing.T, src, expected string) { } func TestFromTestrt(t *testing.T) { - cltest.FromDir(t, "", "./_testrt", true) + cltest.FromDir(t, "callback", "./_testrt", true) } func TestFromTestdata(t *testing.T) { diff --git a/ssa/cl_test.go b/ssa/cl_test.go index a2046fc2..0a9d8493 100644 --- a/ssa/cl_test.go +++ b/ssa/cl_test.go @@ -35,6 +35,33 @@ func TestRuntime(t *testing.T) { cltest.Pkg(t, "github.com/goplus/llgo/internal/abi", "../internal/abi/llgo_autogen.ll") } +/* +func TestCallback(t *testing.T) { + ctx := llvm.NewContext() + mod := ctx.NewModule("foo/bar") + + tc := llvm.FunctionType(ctx.VoidType(), nil, false) + callback := llvm.PointerType(tc, 0) + params := []llvm.Type{callback} + + tfn := llvm.FunctionType(ctx.VoidType(), params, false) + f := llvm.AddFunction(mod, "fn", tfn) + b := ctx.NewBuilder() + blk := llvm.AddBasicBlock(f, "") + b.SetInsertPointAtEnd(blk) + + arg := f.Param(0) + // arg = b.CreateLoad(tc, arg, "") + b.CreateCall(tc, arg, nil, "") + b.CreateRetVoid() + + expected := `; ModuleID = 'foo/bar' +` + if v := mod.String(); v != expected { + t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected) + } +} + /* func TestMap(t *testing.T) { var m typeutil.Map diff --git a/ssa/decl.go b/ssa/decl.go index aae14e79..46069265 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -124,8 +124,9 @@ func (g Global) Init(v Expr) { // respectively, and is nil in the generic method. type aFunction struct { Expr - pkg Package - prog Program + Pkg Package + Prog Program + blks []BasicBlock params []Type @@ -162,7 +163,7 @@ func (p Function) Param(i int) Expr { // NewBuilder creates a new Builder for the function. func (p Function) NewBuilder() Builder { - prog := p.prog + prog := p.Prog b := prog.ctx.NewBuilder() // TODO(xsw): Finalize may cause panic, so comment it. // b.Finalize() diff --git a/ssa/expr.go b/ssa/expr.go index 66d434fc..24169ffd 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -183,7 +183,7 @@ func (b Builder) CStr(v string) Expr { func (b Builder) Str(v string) (ret Expr) { prog := b.Prog cstr := b.CStr(v) - ret = b.InlineCall(b.fn.pkg.rtFunc("NewString"), cstr, prog.Val(len(v))) + ret = b.InlineCall(b.Func.Pkg.rtFunc("NewString"), cstr, prog.Val(len(v))) ret.Type = prog.String() return } @@ -296,7 +296,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr { switch kind { case vkString: if op == token.ADD { - pkg := b.fn.pkg + pkg := b.Func.Pkg return b.InlineCall(pkg.rtFunc("StringCat"), x, y) } case vkComplex: @@ -566,7 +566,7 @@ func (b Builder) IndexAddr(x, idx Expr) Expr { pt := prog.Pointer(telem) switch x.raw.Type.Underlying().(type) { case *types.Slice: - pkg := b.fn.pkg + pkg := b.Func.Pkg ptr := b.InlineCall(pkg.rtFunc("SliceData"), x) indices := []llvm.Value{idx.impl} return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt} @@ -596,7 +596,7 @@ func (b Builder) Index(x, idx Expr, addr func(Expr) Expr) Expr { panic(fmt.Errorf("invalid operation: cannot index %v", t)) } telem = prog.rawType(types.Typ[types.Byte]) - pkg := b.fn.pkg + pkg := b.Func.Pkg ptr = b.InlineCall(pkg.rtFunc("StringData"), x) case *types.Array: telem = prog.Index(x.Type) @@ -631,7 +631,7 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) { log.Printf("Slice %v, %v, %v\n", x.impl, low.impl, high.impl) } prog := b.Prog - pkg := b.fn.pkg + pkg := b.Func.Pkg var nCap Expr var nEltSize Expr var base Expr @@ -693,7 +693,7 @@ func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) { if debugInstr { log.Printf("MakeMap %v, %v\n", t.RawType(), nReserve.impl) } - pkg := b.fn.pkg + pkg := b.Func.Pkg ret.Type = t ret.impl = b.InlineCall(pkg.rtFunc("MakeSmallMap")).impl // TODO(xsw): nReserve @@ -718,7 +718,7 @@ func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) { if debugInstr { log.Printf("MakeSlice %v, %v, %v\n", t.RawType(), len.impl, cap.impl) } - pkg := b.fn.pkg + pkg := b.Func.Pkg if cap.IsNil() { cap = len } @@ -756,7 +756,7 @@ func (b Builder) Alloc(elem Type, heap bool) (ret Expr) { log.Printf("Alloc %v, %v\n", elem.RawType(), heap) } prog := b.Prog - pkg := b.fn.pkg + pkg := b.Func.Pkg size := b.SizeOf(elem) if heap { ret = b.InlineCall(pkg.rtFunc("AllocZ"), size) @@ -797,7 +797,7 @@ func (b Builder) AllocaCStr(gostr Expr) (ret Expr) { if debugInstr { log.Printf("AllocaCStr %v\n", gostr.impl) } - pkg := b.fn.pkg + pkg := b.Func.Pkg n := b.InlineCall(pkg.rtFunc("StringLen"), gostr) n1 := b.BinOp(token.ADD, n, b.Prog.Val(1)) cstr := b.Alloca(n1) @@ -931,7 +931,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr, mayDelay bool) (ret Expr) { isAny := tiund.Empty() fnDo := func() Expr { prog := b.Prog - pkg := b.fn.pkg + pkg := b.Func.Pkg switch tx := x.raw.Type.Underlying().(type) { case *types.Basic: kind := tx.Kind() @@ -998,7 +998,7 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { } switch assertedTyp.kind { case vkSigned, vkUnsigned, vkFloat: - pkg := b.fn.pkg + pkg := b.Func.Pkg fnName := "I2Int" if commaOk { fnName = "CheckI2Int" @@ -1051,20 +1051,25 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { } log.Println(b.String()) } + var ll llvm.Type var sig *types.Signature var raw = fn.raw.Type switch fn.kind { case vkClosure: - fn := b.Field(fn, 0) + fn = b.Field(fn, 0) raw = fn.raw.Type fallthrough - case vkFunc: + case vkFuncPtr: sig = raw.(*types.Signature) - ret.Type = prog.retType(sig) + ll = prog.FuncDecl(sig, InC).ll + case vkFuncDecl: + sig = raw.(*types.Signature) + ll = fn.ll default: panic("unreachable") } - ret.impl = llvm.CreateCall(b.impl, fn.ll, fn.impl, llvmValues(args, sig.Params(), b)) + ret.Type = prog.retType(sig) + ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmValues(args, sig.Params(), b)) return } @@ -1081,10 +1086,10 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { arg := args[0] switch t := arg.raw.Type.Underlying().(type) { case *types.Slice: - return b.InlineCall(b.fn.pkg.rtFunc("SliceLen"), arg) + return b.InlineCall(b.Func.Pkg.rtFunc("SliceLen"), arg) case *types.Basic: if t.Kind() == types.String { - return b.InlineCall(b.fn.pkg.rtFunc("StringLen"), arg) + return b.InlineCall(b.Func.Pkg.rtFunc("StringLen"), arg) } } } @@ -1093,7 +1098,7 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { arg := args[0] switch arg.raw.Type.Underlying().(type) { case *types.Slice: - return b.InlineCall(b.fn.pkg.rtFunc("SliceCap"), arg) + return b.InlineCall(b.Func.Pkg.rtFunc("SliceCap"), arg) } } } diff --git a/ssa/package.go b/ssa/package.go index f75e0e84..89277bc6 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -312,7 +312,7 @@ type aPackage struct { mod llvm.Module fns map[string]Function vars map[string]Global - prog Program + Prog Program } type Package = *aPackage @@ -326,7 +326,7 @@ func (p Package) NewConst(name string, val constant.Value) NamedConst { // NewVar creates a new global variable. func (p Package) NewVar(name string, typ types.Type, bg Background) Global { - t := p.prog.Type(typ, bg) + t := p.Prog.Type(typ, bg) gbl := llvm.AddGlobal(p.mod, t.ll, name) ret := &aGlobal{Expr{gbl, t}} p.vars[name] = ret @@ -343,18 +343,18 @@ func (p Package) NewFunc(name string, sig *types.Signature, bg Background) Funct if v, ok := p.fns[name]; ok { return v } - t := p.prog.FuncDecl(sig, bg) + t := p.Prog.FuncDecl(sig, bg) if debugInstr { log.Println("NewFunc", name, t.raw.Type) } fn := llvm.AddFunction(p.mod, name, t.ll) - ret := newFunction(fn, t, p, p.prog) + ret := newFunction(fn, t, p, p.Prog) p.fns[name] = ret return ret } func (p Package) rtFunc(fnName string) Expr { - fn := p.prog.runtime().Scope().Lookup(fnName).(*types.Func) + fn := p.Prog.runtime().Scope().Lookup(fnName).(*types.Func) name := FullName(fn.Pkg(), fnName) sig := fn.Type().(*types.Signature) return p.NewFunc(name, sig, InGo).Expr diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index f6978845..9ae93467 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -50,7 +50,7 @@ func (p BasicBlock) Index() int { type aBuilder struct { impl llvm.Builder - fn Function + Func Function Prog Program } @@ -59,7 +59,7 @@ type Builder = *aBuilder // SetBlock sets the current block to the specified basic block. func (b Builder) SetBlock(blk BasicBlock) Builder { - if b.fn != blk.fn { + if b.Func != blk.fn { panic("mismatched function") } if debugInstr { @@ -74,7 +74,7 @@ func (b Builder) Panic(v Expr) { if debugInstr { log.Printf("Panic %v\n", v.impl) } - pkg := b.fn.pkg + pkg := b.Func.Pkg b.Call(pkg.rtFunc("TracePanic"), v) b.impl.CreateUnreachable() } @@ -103,14 +103,14 @@ func (b Builder) Return(results ...Expr) { case 1: b.impl.CreateRet(results[0].impl) default: - tret := b.fn.raw.Type.(*types.Signature).Results() + tret := b.Func.raw.Type.(*types.Signature).Results() b.impl.CreateAggregateRet(llvmValues(results, tret, b)) } } // Jump emits a jump instruction. func (b Builder) Jump(jmpb BasicBlock) { - if b.fn != jmpb.fn { + if b.Func != jmpb.fn { panic("mismatched function") } if debugInstr { @@ -121,7 +121,7 @@ func (b Builder) Jump(jmpb BasicBlock) { // If emits an if instruction. func (b Builder) If(cond Expr, thenb, elseb BasicBlock) { - if b.fn != thenb.fn || b.fn != elseb.fn { + if b.Func != thenb.fn || b.Func != elseb.fn { panic("mismatched function") } if debugInstr { diff --git a/ssa/type.go b/ssa/type.go index ae67cfc4..a71f878f 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -40,7 +40,8 @@ const ( vkString vkBool vkPtr - vkFunc + vkFuncDecl + vkFuncPtr vkClosure vkTuple vkDelayExpr = -1 @@ -241,7 +242,7 @@ func (p Program) toType(raw types.Type) Type { case *types.Named: return p.toNamed(t) case *types.Signature: // represents a C function pointer in raw type - return &aType{p.toLLVMFuncPtr(t), typ, vkFunc} + return &aType{p.toLLVMFuncPtr(t), typ, vkFuncPtr} case *types.Array: elem := p.rawType(t.Elem()) return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid} diff --git a/ssa/type_cvt.go b/ssa/type_cvt.go index e32abf3d..cd6a598f 100644 --- a/ssa/type_cvt.go +++ b/ssa/type_cvt.go @@ -56,7 +56,7 @@ func (p Program) FuncDecl(sig *types.Signature, bg Background) Type { if bg == InGo { sig = p.gocvt.cvtFunc(sig, true) } - return &aType{p.toLLVMFunc(sig), rawType{sig}, vkFunc} + return &aType{p.toLLVMFunc(sig), rawType{sig}, vkFuncDecl} } func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {