vkFuncDecl, vkFuncPtr, vkClosure; callback example

This commit is contained in:
xushiwei
2024-05-05 15:59:33 +08:00
parent 03a194a514
commit 067cf0cba6
11 changed files with 136 additions and 38 deletions

15
cl/_testrt/callback/in.go Normal file
View File

@@ -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"))
})
}

View File

@@ -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, ...)

View File

@@ -22,10 +22,10 @@ _llgo_1: ; preds = %_llgo_2, %_llgo_0
_llgo_2: ; preds = %_llgo_1 _llgo_2: ; preds = %_llgo_1
%9 = extractvalue { ptr, ptr } %1, 0 %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) %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 %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 br label %_llgo_1
_llgo_3: ; preds = %_llgo_1 _llgo_3: ; preds = %_llgo_1

View File

@@ -29,7 +29,7 @@ func testCompile(t *testing.T, src, expected string) {
} }
func TestFromTestrt(t *testing.T) { func TestFromTestrt(t *testing.T) {
cltest.FromDir(t, "", "./_testrt", true) cltest.FromDir(t, "callback", "./_testrt", true)
} }
func TestFromTestdata(t *testing.T) { func TestFromTestdata(t *testing.T) {

View File

@@ -35,6 +35,33 @@ func TestRuntime(t *testing.T) {
cltest.Pkg(t, "github.com/goplus/llgo/internal/abi", "../internal/abi/llgo_autogen.ll") 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) { func TestMap(t *testing.T) {
var m typeutil.Map var m typeutil.Map

View File

@@ -124,8 +124,9 @@ func (g Global) Init(v Expr) {
// respectively, and is nil in the generic method. // respectively, and is nil in the generic method.
type aFunction struct { type aFunction struct {
Expr Expr
pkg Package Pkg Package
prog Program Prog Program
blks []BasicBlock blks []BasicBlock
params []Type params []Type
@@ -162,7 +163,7 @@ func (p Function) Param(i int) Expr {
// NewBuilder creates a new Builder for the function. // NewBuilder creates a new Builder for the function.
func (p Function) NewBuilder() Builder { func (p Function) NewBuilder() Builder {
prog := p.prog prog := p.Prog
b := prog.ctx.NewBuilder() b := prog.ctx.NewBuilder()
// TODO(xsw): Finalize may cause panic, so comment it. // TODO(xsw): Finalize may cause panic, so comment it.
// b.Finalize() // b.Finalize()

View File

@@ -183,7 +183,7 @@ func (b Builder) CStr(v string) Expr {
func (b Builder) Str(v string) (ret Expr) { func (b Builder) Str(v string) (ret Expr) {
prog := b.Prog prog := b.Prog
cstr := b.CStr(v) 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() ret.Type = prog.String()
return return
} }
@@ -296,7 +296,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr {
switch kind { switch kind {
case vkString: case vkString:
if op == token.ADD { if op == token.ADD {
pkg := b.fn.pkg pkg := b.Func.Pkg
return b.InlineCall(pkg.rtFunc("StringCat"), x, y) return b.InlineCall(pkg.rtFunc("StringCat"), x, y)
} }
case vkComplex: case vkComplex:
@@ -566,7 +566,7 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
pt := prog.Pointer(telem) pt := prog.Pointer(telem)
switch x.raw.Type.Underlying().(type) { switch x.raw.Type.Underlying().(type) {
case *types.Slice: case *types.Slice:
pkg := b.fn.pkg pkg := b.Func.Pkg
ptr := b.InlineCall(pkg.rtFunc("SliceData"), x) ptr := b.InlineCall(pkg.rtFunc("SliceData"), x)
indices := []llvm.Value{idx.impl} indices := []llvm.Value{idx.impl}
return Expr{llvm.CreateInBoundsGEP(b.impl, telem.ll, ptr.impl, indices), pt} 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)) panic(fmt.Errorf("invalid operation: cannot index %v", t))
} }
telem = prog.rawType(types.Typ[types.Byte]) telem = prog.rawType(types.Typ[types.Byte])
pkg := b.fn.pkg pkg := b.Func.Pkg
ptr = b.InlineCall(pkg.rtFunc("StringData"), x) ptr = b.InlineCall(pkg.rtFunc("StringData"), x)
case *types.Array: case *types.Array:
telem = prog.Index(x.Type) 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) log.Printf("Slice %v, %v, %v\n", x.impl, low.impl, high.impl)
} }
prog := b.Prog prog := b.Prog
pkg := b.fn.pkg pkg := b.Func.Pkg
var nCap Expr var nCap Expr
var nEltSize Expr var nEltSize Expr
var base Expr var base Expr
@@ -693,7 +693,7 @@ func (b Builder) MakeMap(t Type, nReserve Expr) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("MakeMap %v, %v\n", t.RawType(), nReserve.impl) log.Printf("MakeMap %v, %v\n", t.RawType(), nReserve.impl)
} }
pkg := b.fn.pkg pkg := b.Func.Pkg
ret.Type = t ret.Type = t
ret.impl = b.InlineCall(pkg.rtFunc("MakeSmallMap")).impl ret.impl = b.InlineCall(pkg.rtFunc("MakeSmallMap")).impl
// TODO(xsw): nReserve // TODO(xsw): nReserve
@@ -718,7 +718,7 @@ func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("MakeSlice %v, %v, %v\n", t.RawType(), len.impl, cap.impl) log.Printf("MakeSlice %v, %v, %v\n", t.RawType(), len.impl, cap.impl)
} }
pkg := b.fn.pkg pkg := b.Func.Pkg
if cap.IsNil() { if cap.IsNil() {
cap = len 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) log.Printf("Alloc %v, %v\n", elem.RawType(), heap)
} }
prog := b.Prog prog := b.Prog
pkg := b.fn.pkg pkg := b.Func.Pkg
size := b.SizeOf(elem) size := b.SizeOf(elem)
if heap { if heap {
ret = b.InlineCall(pkg.rtFunc("AllocZ"), size) ret = b.InlineCall(pkg.rtFunc("AllocZ"), size)
@@ -797,7 +797,7 @@ func (b Builder) AllocaCStr(gostr Expr) (ret Expr) {
if debugInstr { if debugInstr {
log.Printf("AllocaCStr %v\n", gostr.impl) log.Printf("AllocaCStr %v\n", gostr.impl)
} }
pkg := b.fn.pkg pkg := b.Func.Pkg
n := b.InlineCall(pkg.rtFunc("StringLen"), gostr) n := b.InlineCall(pkg.rtFunc("StringLen"), gostr)
n1 := b.BinOp(token.ADD, n, b.Prog.Val(1)) n1 := b.BinOp(token.ADD, n, b.Prog.Val(1))
cstr := b.Alloca(n1) cstr := b.Alloca(n1)
@@ -931,7 +931,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr, mayDelay bool) (ret Expr) {
isAny := tiund.Empty() isAny := tiund.Empty()
fnDo := func() Expr { fnDo := func() Expr {
prog := b.Prog prog := b.Prog
pkg := b.fn.pkg pkg := b.Func.Pkg
switch tx := x.raw.Type.Underlying().(type) { switch tx := x.raw.Type.Underlying().(type) {
case *types.Basic: case *types.Basic:
kind := tx.Kind() kind := tx.Kind()
@@ -998,7 +998,7 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) {
} }
switch assertedTyp.kind { switch assertedTyp.kind {
case vkSigned, vkUnsigned, vkFloat: case vkSigned, vkUnsigned, vkFloat:
pkg := b.fn.pkg pkg := b.Func.Pkg
fnName := "I2Int" fnName := "I2Int"
if commaOk { if commaOk {
fnName = "CheckI2Int" fnName = "CheckI2Int"
@@ -1051,20 +1051,25 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
} }
log.Println(b.String()) log.Println(b.String())
} }
var ll llvm.Type
var sig *types.Signature var sig *types.Signature
var raw = fn.raw.Type var raw = fn.raw.Type
switch fn.kind { switch fn.kind {
case vkClosure: case vkClosure:
fn := b.Field(fn, 0) fn = b.Field(fn, 0)
raw = fn.raw.Type raw = fn.raw.Type
fallthrough fallthrough
case vkFunc: case vkFuncPtr:
sig = raw.(*types.Signature) 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: default:
panic("unreachable") 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 return
} }
@@ -1081,10 +1086,10 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) {
arg := args[0] arg := args[0]
switch t := arg.raw.Type.Underlying().(type) { switch t := arg.raw.Type.Underlying().(type) {
case *types.Slice: case *types.Slice:
return b.InlineCall(b.fn.pkg.rtFunc("SliceLen"), arg) return b.InlineCall(b.Func.Pkg.rtFunc("SliceLen"), arg)
case *types.Basic: case *types.Basic:
if t.Kind() == types.String { 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] arg := args[0]
switch arg.raw.Type.Underlying().(type) { switch arg.raw.Type.Underlying().(type) {
case *types.Slice: case *types.Slice:
return b.InlineCall(b.fn.pkg.rtFunc("SliceCap"), arg) return b.InlineCall(b.Func.Pkg.rtFunc("SliceCap"), arg)
} }
} }
} }

View File

@@ -312,7 +312,7 @@ type aPackage struct {
mod llvm.Module mod llvm.Module
fns map[string]Function fns map[string]Function
vars map[string]Global vars map[string]Global
prog Program Prog Program
} }
type Package = *aPackage type Package = *aPackage
@@ -326,7 +326,7 @@ func (p Package) NewConst(name string, val constant.Value) NamedConst {
// NewVar creates a new global variable. // NewVar creates a new global variable.
func (p Package) NewVar(name string, typ types.Type, bg Background) Global { 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) gbl := llvm.AddGlobal(p.mod, t.ll, name)
ret := &aGlobal{Expr{gbl, t}} ret := &aGlobal{Expr{gbl, t}}
p.vars[name] = ret 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 { if v, ok := p.fns[name]; ok {
return v return v
} }
t := p.prog.FuncDecl(sig, bg) t := p.Prog.FuncDecl(sig, bg)
if debugInstr { if debugInstr {
log.Println("NewFunc", name, t.raw.Type) log.Println("NewFunc", name, t.raw.Type)
} }
fn := llvm.AddFunction(p.mod, name, t.ll) 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 p.fns[name] = ret
return ret return ret
} }
func (p Package) rtFunc(fnName string) Expr { 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) name := FullName(fn.Pkg(), fnName)
sig := fn.Type().(*types.Signature) sig := fn.Type().(*types.Signature)
return p.NewFunc(name, sig, InGo).Expr return p.NewFunc(name, sig, InGo).Expr

View File

@@ -50,7 +50,7 @@ func (p BasicBlock) Index() int {
type aBuilder struct { type aBuilder struct {
impl llvm.Builder impl llvm.Builder
fn Function Func Function
Prog Program Prog Program
} }
@@ -59,7 +59,7 @@ type Builder = *aBuilder
// SetBlock sets the current block to the specified basic block. // SetBlock sets the current block to the specified basic block.
func (b Builder) SetBlock(blk BasicBlock) Builder { func (b Builder) SetBlock(blk BasicBlock) Builder {
if b.fn != blk.fn { if b.Func != blk.fn {
panic("mismatched function") panic("mismatched function")
} }
if debugInstr { if debugInstr {
@@ -74,7 +74,7 @@ func (b Builder) Panic(v Expr) {
if debugInstr { if debugInstr {
log.Printf("Panic %v\n", v.impl) log.Printf("Panic %v\n", v.impl)
} }
pkg := b.fn.pkg pkg := b.Func.Pkg
b.Call(pkg.rtFunc("TracePanic"), v) b.Call(pkg.rtFunc("TracePanic"), v)
b.impl.CreateUnreachable() b.impl.CreateUnreachable()
} }
@@ -103,14 +103,14 @@ func (b Builder) Return(results ...Expr) {
case 1: case 1:
b.impl.CreateRet(results[0].impl) b.impl.CreateRet(results[0].impl)
default: default:
tret := b.fn.raw.Type.(*types.Signature).Results() tret := b.Func.raw.Type.(*types.Signature).Results()
b.impl.CreateAggregateRet(llvmValues(results, tret, b)) b.impl.CreateAggregateRet(llvmValues(results, tret, b))
} }
} }
// Jump emits a jump instruction. // Jump emits a jump instruction.
func (b Builder) Jump(jmpb BasicBlock) { func (b Builder) Jump(jmpb BasicBlock) {
if b.fn != jmpb.fn { if b.Func != jmpb.fn {
panic("mismatched function") panic("mismatched function")
} }
if debugInstr { if debugInstr {
@@ -121,7 +121,7 @@ func (b Builder) Jump(jmpb BasicBlock) {
// If emits an if instruction. // If emits an if instruction.
func (b Builder) If(cond Expr, thenb, elseb BasicBlock) { 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") panic("mismatched function")
} }
if debugInstr { if debugInstr {

View File

@@ -40,7 +40,8 @@ const (
vkString vkString
vkBool vkBool
vkPtr vkPtr
vkFunc vkFuncDecl
vkFuncPtr
vkClosure vkClosure
vkTuple vkTuple
vkDelayExpr = -1 vkDelayExpr = -1
@@ -241,7 +242,7 @@ func (p Program) toType(raw types.Type) Type {
case *types.Named: case *types.Named:
return p.toNamed(t) return p.toNamed(t)
case *types.Signature: // represents a C function pointer in raw type 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: case *types.Array:
elem := p.rawType(t.Elem()) elem := p.rawType(t.Elem())
return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid} return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid}

View File

@@ -56,7 +56,7 @@ func (p Program) FuncDecl(sig *types.Signature, bg Background) Type {
if bg == InGo { if bg == InGo {
sig = p.gocvt.cvtFunc(sig, true) 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) { func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {