llgo/ssa: unsafeInterface; runtime.PointerTo

This commit is contained in:
xushiwei
2024-05-27 08:07:34 +08:00
parent 12b0d81dda
commit 165a99fd83
8 changed files with 220 additions and 89 deletions

View File

@@ -558,6 +558,12 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
switch v := iv.(type) { switch v := iv.(type) {
case *ssa.Call: case *ssa.Call:
cv := v.Call.Value cv := v.Call.Value
if mthd := v.Call.Method; mthd != nil {
o := p.compileValue(b, cv)
args := p.compileValues(b, v.Call.Args, fnNormal)
ret = b.Icall(o, v.Call.Method, args...)
break
}
kind := p.funcKind(cv) kind := p.funcKind(cv)
if kind == fnIgnore { if kind == fnIgnore {
return return

View File

@@ -43,6 +43,8 @@ type (
Itab = itab Itab = itab
) )
type Imethod = abi.Imethod
type Method = abi.Method
type FuncType = abi.FuncType type FuncType = abi.FuncType
type InterfaceType = abi.InterfaceType type InterfaceType = abi.InterfaceType
@@ -55,13 +57,104 @@ func ToEface(i Iface) Eface {
const ( const (
typeHdrSize = unsafe.Sizeof(abi.Type{}) typeHdrSize = unsafe.Sizeof(abi.Type{})
arrayTypeHdrSize = unsafe.Sizeof(abi.ArrayType{})
chanTypeHdrSize = unsafe.Sizeof(abi.ChanType{})
funcTypeHdrSize = unsafe.Sizeof(abi.FuncType{}) funcTypeHdrSize = unsafe.Sizeof(abi.FuncType{})
interfaceTypeHdrSize = unsafe.Sizeof(abi.InterfaceType{})
mapTypeHdrSize = unsafe.Sizeof(abi.MapType{})
ptrTypeHdrSize = unsafe.Sizeof(abi.PtrType{})
sliceTypeHdrSize = unsafe.Sizeof(abi.SliceType{})
structTypeHdrSize = unsafe.Sizeof(abi.StructType{})
uncommonTypeHdrSize = unsafe.Sizeof(abi.UncommonType{}) uncommonTypeHdrSize = unsafe.Sizeof(abi.UncommonType{})
methodSize = unsafe.Sizeof(abi.Method{}) methodSize = unsafe.Sizeof(abi.Method{})
pointerSize = unsafe.Sizeof(uintptr(0)) pointerSize = unsafe.Sizeof(uintptr(0))
itabHdrSize = unsafe.Sizeof(itab{}) - pointerSize itabHdrSize = unsafe.Sizeof(itab{}) - pointerSize
) )
var hdrSizes = [...]uintptr{
arrayTypeHdrSize,
chanTypeHdrSize,
funcTypeHdrSize,
interfaceTypeHdrSize,
mapTypeHdrSize,
ptrTypeHdrSize,
sliceTypeHdrSize,
typeHdrSize,
structTypeHdrSize,
}
func hdrSizeOf(kind abi.Kind) uintptr {
if kind >= abi.Array && kind <= abi.Struct {
return hdrSizes[kind-abi.Array]
}
return typeHdrSize
}
// Named returns a named type.
func Named(pkgPath, name string, underlying *Type, methods []abi.Method) *Type {
tflag := underlying.TFlag
if tflag&abi.TFlagUncommon != 0 {
panic("runtime: underlying type is already named")
}
kind := underlying.Kind()
n := len(methods)
if kind == abi.Interface {
if n > 0 {
panic("runtime: interface type cannot have methods")
}
ret := *underlying.InterfaceType()
ret.PkgPath_ = pkgPath
ret.Str_ = name
return &ret.Type
}
baseSize := hdrSizeOf(kind)
extraSize := uintptr(0)
if kind == abi.Func {
f := underlying.FuncType()
extraSize = uintptr(f.In()+f.Out()) * pointerSize
}
size := baseSize + extraSize
if n > 0 || pkgPath != "" {
size += uncommonTypeHdrSize + uintptr(n)*methodSize
tflag |= abi.TFlagUncommon
}
ptr := AllocU(size)
c.Memcpy(ptr, unsafe.Pointer(underlying), baseSize)
ret := (*Type)(ptr)
ret.TFlag = tflag | abi.TFlagNamed
ret.Str_ = name
xcount := 0
for _, m := range methods {
if !m.Exported() {
break
}
xcount++
}
uncommon := (*abi.UncommonType)(c.Advance(ptr, int(baseSize)))
uncommon.PkgPath_ = pkgPath
uncommon.Mcount = uint16(n)
uncommon.Xcount = uint16(xcount)
uncommon.Moff = uint32(uncommonTypeHdrSize + extraSize)
extraOff := int(baseSize + uncommonTypeHdrSize)
if extraSize > 0 {
src := c.Advance(unsafe.Pointer(underlying), int(baseSize))
dest := c.Advance(unsafe.Pointer(ptr), extraOff)
c.Memcpy(dest, src, extraSize)
extraOff += int(extraSize)
}
data := (*abi.Method)(c.Advance(ptr, extraOff))
copy(unsafe.Slice(data, n), methods)
return ret
}
// Func returns a function type. // Func returns a function type.
func Func(in, out []*Type, variadic bool) *FuncType { func Func(in, out []*Type, variadic bool) *FuncType {
n := len(in) + len(out) n := len(in) + len(out)
@@ -85,61 +178,8 @@ func Func(in, out []*Type, variadic bool) *FuncType {
return ret return ret
} }
// Imethod returns an interface method.
func Imethod(name string, typ *FuncType) abi.Imethod {
return abi.Imethod{
Name_: name,
Typ_: typ,
}
}
// Method returns a method.
func Method(name string, typ *FuncType, ifn, tfn abi.Text) abi.Method {
return abi.Method{
Name_: name,
Mtyp_: typ,
Ifn_: ifn,
Tfn_: tfn,
}
}
// Named returns a named type.
func Named(pkgPath, name string, underlying *Type, methods []abi.Method) *Type {
tflag := underlying.TFlag
size := typeHdrSize
n := len(methods)
if n > 0 || pkgPath != "" {
size += uncommonTypeHdrSize + uintptr(n)*methodSize
tflag |= abi.TFlagUncommon
}
ptr := AllocU(size)
ret := (*Type)(ptr)
*ret = *underlying
ret.TFlag = tflag | abi.TFlagNamed
ret.Str_ = name
xcount := 0
for _, m := range methods {
if !m.Exported() {
break
}
xcount++
}
uncommon := (*abi.UncommonType)(c.Advance(ptr, int(typeHdrSize)))
uncommon.PkgPath_ = pkgPath
uncommon.Mcount = uint16(n)
uncommon.Xcount = uint16(xcount)
uncommon.Moff = uint32(uncommonTypeHdrSize)
data := (*abi.Method)(c.Advance(ptr, int(typeHdrSize+uncommonTypeHdrSize)))
copy(unsafe.Slice(data, n), methods)
return ret
}
// Interface returns an interface type. // Interface returns an interface type.
func Interface(pkgPath string, methods []abi.Imethod) *Type { func Interface(pkgPath string, methods []abi.Imethod) *InterfaceType {
ret := &abi.InterfaceType{ ret := &abi.InterfaceType{
Type: Type{ Type: Type{
Size_: unsafe.Sizeof(eface{}), Size_: unsafe.Sizeof(eface{}),
@@ -149,7 +189,7 @@ func Interface(pkgPath string, methods []abi.Imethod) *Type {
PkgPath_: pkgPath, PkgPath_: pkgPath,
Methods: methods, Methods: methods,
} }
return &ret.Type return ret
} }
// NewItab returns a new itab. // NewItab returns a new itab.
@@ -158,7 +198,7 @@ func NewItab(inter *InterfaceType, typ *Type) *Itab {
size := itabHdrSize + uintptr(n)*pointerSize size := itabHdrSize + uintptr(n)*pointerSize
ptr := AllocU(size) ptr := AllocU(size)
ret := (*Itab)(ptr) ret := (*itab)(ptr)
ret.inter = inter ret.inter = inter
ret._type = typ ret._type = typ
ret.hash = typ.Hash ret.hash = typ.Hash

View File

@@ -112,9 +112,11 @@ func Struct(pkgPath string, size uintptr, fields ...abi.StructField) *Type {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Pointer returns a pointer type. // PointerTo returns the pointer type with element elem.
func Pointer(elem *Type) *Type { func PointerTo(elem *Type) *Type {
ret := &abi.PtrType{ ret := elem.PtrToThis_
if ret == nil {
ptr := &abi.PtrType{
Type: Type{ Type: Type{
Size_: unsafe.Sizeof(uintptr(0)), Size_: unsafe.Sizeof(uintptr(0)),
Hash: uint32(abi.Pointer), // TODO(xsw): hash Hash: uint32(abi.Pointer), // TODO(xsw): hash
@@ -122,7 +124,10 @@ func Pointer(elem *Type) *Type {
}, },
Elem: elem, Elem: elem,
} }
return &ret.Type ret = &ptr.Type
elem.PtrToThis_ = ret
}
return ret
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -26,7 +26,7 @@ import (
) )
func TestFromTestgo(t *testing.T) { func TestFromTestgo(t *testing.T) {
cltest.FromDir(t, "", "../cl/_testgo", false) cltest.FromDir(t, "errors", "../cl/_testgo", false)
} }
func TestFromTestpy(t *testing.T) { func TestFromTestpy(t *testing.T) {

View File

@@ -193,12 +193,6 @@ func (b Builder) Str(v string) (ret Expr) {
return Expr{aggregateValue(b.impl, prog.rtString(), data, size), prog.String()} return Expr{aggregateValue(b.impl, prog.rtString(), data, size), prog.String()}
} }
func (b Builder) pkgName(pkgPath string) Expr {
// TODO(xsw): use a global cache
// return b.Call(b.Pkg.rtFunc("NewPkgName"), b.Str(pkgPath))
return b.Str(pkgPath)
}
// unsafeString(data *byte, size int) string // unsafeString(data *byte, size int) string
func (b Builder) unsafeString(data, size llvm.Value) Expr { func (b Builder) unsafeString(data, size llvm.Value) Expr {
prog := b.Prog prog := b.Prog
@@ -906,7 +900,16 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
return b.Call(fn, args...) return b.Call(fn, args...)
} }
// The Call instruction represents a function or method call. // The Icall instruction represents a interface method call.
//
// Example printed form:
//
// t7 = invoke t5.Println(...t6)
func (b Builder) Icall(o Expr, method *types.Func, args ...Expr) (ret Expr) {
panic("todo")
}
// The Call instruction represents a function call.
// //
// The Call instruction yields the function result if there is exactly // The Call instruction yields the function result if there is exactly
// one. Otherwise it returns a tuple, the components of which are // one. Otherwise it returns a tuple, the components of which are
@@ -916,7 +919,6 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) {
// //
// t2 = println(t0, t1) // t2 = println(t0, t1)
// t4 = t3() // t4 = t3()
// t7 = invoke t5.Println(...t6)
func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
if debugInstr { if debugInstr {
var b bytes.Buffer var b bytes.Buffer
@@ -954,7 +956,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) {
sig = raw.(*types.Signature) sig = raw.(*types.Signature)
ll = fn.ll ll = fn.ll
default: default:
panic("unreachable") log.Panicf("unreachable: %d(%T)\n", kind, raw)
} }
ret.Type = prog.retType(sig) ret.Type = prog.retType(sig)
ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b)) ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b))

View File

@@ -55,23 +55,74 @@ func (b Builder) abiTypeOf(t types.Type) Expr {
return b.abiStructOf(t) return b.abiStructOf(t)
case *types.Named: case *types.Named:
return b.abiNamedOf(t) return b.abiNamedOf(t)
case *types.Interface:
return b.abiInterfaceOf(t)
case *types.Signature:
return b.abiFuncOf(t)
} }
panic("todo") panic("todo")
} }
func (b Builder) abiTupleOf(t *types.Tuple) Expr {
n := t.Len()
prog := b.Prog
tSlice := prog.Slice(prog.AbiTypePtr())
tuple := make([]Expr, n)
for i := 0; i < n; i++ {
tuple[i] = b.abiType(t.At(i).Type())
}
return b.SliceLit(tSlice, tuple...)
}
// func Func(in, out []*Type, variadic bool)
func (b Builder) abiFuncOf(sig *types.Signature) Expr {
prog := b.Prog
pkg := b.Pkg
fn := pkg.rtFunc("Func")
params := b.abiTupleOf(sig.Params())
results := b.abiTupleOf(sig.Results())
variadic := prog.Val(sig.Variadic())
return b.Call(fn, params, results, variadic)
}
// Imethod{name string, typ *FuncType}
func (b Builder) abiImethodOf(m *types.Func) Expr {
prog := b.Prog
name := b.Str(m.Name())
typ := b.abiType(m.Type())
return b.aggregateValue(prog.rtType("Imethod"), name.impl, typ.impl)
}
// func Interface(pkgPath string, methods []abi.Imethod)
func (b Builder) abiInterfaceOf(t *types.Interface) Expr {
prog := b.Prog
n := t.NumMethods()
methods := make([]Expr, n)
for i := 0; i < n; i++ {
m := t.Method(i)
methods[i] = b.abiImethodOf(m)
}
pkg := b.Pkg
fn := pkg.rtFunc("Interface")
pkgPath := pkg.Path()
tSlice := lastParamType(prog, fn)
methodSlice := b.SliceLit(tSlice, methods...)
return b.Call(fn, b.Str(pkgPath), methodSlice)
}
// func Named(pkgPath, name string, underlying *Type, methods []abi.Method) // func Named(pkgPath, name string, underlying *Type, methods []abi.Method)
func (b Builder) abiNamedOf(t *types.Named) Expr { func (b Builder) abiNamedOf(t *types.Named) Expr {
under := b.abiTypeOf(t.Underlying()) under := b.abiTypeOf(t.Underlying())
path := abi.PathOf(t.Obj().Pkg())
name := NameOf(t) name := NameOf(t)
prog := b.Prog prog := b.Prog
pkg := b.Pkg pkg := b.Pkg
pkgPath := b.pkgName(pkg.Path())
fn := pkg.rtFunc("Named") fn := pkg.rtFunc("Named")
tSlice := lastParamType(prog, fn) tSlice := lastParamType(prog, fn)
// TODO(xsw): methods // TODO(xsw): methods
methods := prog.Zero(tSlice) methods := prog.Zero(tSlice)
return b.Call(fn, pkgPath, b.Str(name), under, methods) return b.Call(fn, b.Str(path), b.Str(name), under, methods)
} }
func (b Builder) abiPointerOf(t *types.Pointer) Expr { func (b Builder) abiPointerOf(t *types.Pointer) Expr {
@@ -93,7 +144,7 @@ func (b Builder) abiStructOf(t *types.Struct) Expr {
off := uintptr(prog.OffsetOf(typ, i)) off := uintptr(prog.OffsetOf(typ, i))
flds[i] = b.structField(sfAbi, prog, f, off, t.Tag(i)) flds[i] = b.structField(sfAbi, prog, f, off, t.Tag(i))
} }
pkgPath := b.pkgName(pkg.Path()) pkgPath := b.Str(pkg.Path())
tSlice := lastParamType(prog, strucAbi) tSlice := lastParamType(prog, strucAbi)
fldSlice := b.SliceLit(tSlice, flds...) fldSlice := b.SliceLit(tSlice, flds...)
size := prog.IntVal(prog.SizeOf(typ), prog.Uintptr()) size := prog.IntVal(prog.SizeOf(typ), prog.Uintptr())
@@ -156,6 +207,25 @@ func (b Builder) unsafeEface(t, data llvm.Value) llvm.Value {
return aggregateValue(b.impl, b.Prog.rtEface(), t, data) return aggregateValue(b.impl, b.Prog.rtEface(), t, data)
} }
// unsafeIface(itab *runtime.Itab, data unsafe.Pointer) Eface
func (b Builder) unsafeIface(itab, data llvm.Value) llvm.Value {
return aggregateValue(b.impl, b.Prog.rtIface(), itab, data)
}
// func NewItab(tintf *InterfaceType, typ *Type) *runtime.Itab
func (b Builder) newItab(tintf, typ Expr) Expr {
return b.Call(b.Pkg.rtFunc("NewItab"), tintf, typ)
}
func (b Builder) unsafeInterface(rawIntf *types.Interface, t Expr, data llvm.Value) llvm.Value {
if rawIntf.Empty() {
return b.unsafeEface(t.impl, data)
}
tintf := b.abiType(rawIntf)
itab := b.newItab(tintf, t)
return b.unsafeIface(itab.impl, data)
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// MakeInterface constructs an instance of an interface type from a // MakeInterface constructs an instance of an interface type from a
@@ -185,7 +255,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) {
case abi.Indirect: case abi.Indirect:
vptr := b.AllocU(typ) vptr := b.AllocU(typ)
b.Store(vptr, x) b.Store(vptr, x)
return Expr{b.unsafeEface(tabi.impl, vptr.impl), tinter} return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter}
} }
ximpl := x.impl ximpl := x.impl
if lvl > 0 { if lvl > 0 {
@@ -194,7 +264,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) {
var u llvm.Value var u llvm.Value
switch kind { switch kind {
case abi.Pointer: case abi.Pointer:
return Expr{b.unsafeEface(tabi.impl, ximpl), tinter} return Expr{b.unsafeInterface(rawIntf, tabi, ximpl), tinter}
case abi.Integer: case abi.Integer:
tu := prog.Uintptr() tu := prog.Uintptr()
u = llvm.CreateIntCast(b.impl, ximpl, tu.ll) u = llvm.CreateIntCast(b.impl, ximpl, tu.ll)
@@ -205,7 +275,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) {
panic("todo") panic("todo")
} }
data := llvm.CreateIntToPtr(b.impl, u, prog.tyVoidPtr()) data := llvm.CreateIntToPtr(b.impl, u, prog.tyVoidPtr())
return Expr{b.unsafeEface(tabi.impl, data), tinter} return Expr{b.unsafeInterface(rawIntf, tabi, data), tinter}
} }
func (b Builder) valFromData(typ Type, data llvm.Value) Expr { func (b Builder) valFromData(typ Type, data llvm.Value) Expr {

View File

@@ -125,6 +125,7 @@ type aProgram struct {
rtStringTy llvm.Type rtStringTy llvm.Type
rtEfaceTy llvm.Type rtEfaceTy llvm.Type
rtIfaceTy llvm.Type
rtSliceTy llvm.Type rtSliceTy llvm.Type
rtMapTy llvm.Type rtMapTy llvm.Type
@@ -149,7 +150,6 @@ type aProgram struct {
pyObjPPtr Type pyObjPPtr Type
abiTyptr Type abiTyptr Type
abiTypptr Type abiTypptr Type
//efaceTy Type
pyImpTy *types.Signature pyImpTy *types.Signature
pyNewList *types.Signature pyNewList *types.Signature
@@ -256,6 +256,13 @@ func (p Program) rtEface() llvm.Type {
return p.rtEfaceTy return p.rtEfaceTy
} }
func (p Program) rtIface() llvm.Type {
if p.rtIfaceTy.IsNil() {
p.rtIfaceTy = p.rtType("Iface").ll
}
return p.rtIfaceTy
}
func (p Program) rtMap() llvm.Type { func (p Program) rtMap() llvm.Type {
if p.rtMapTy.IsNil() { if p.rtMapTy.IsNil() {
p.rtMapTy = p.rtType("Map").ll p.rtMapTy = p.rtType("Map").ll

View File

@@ -274,6 +274,7 @@ func (p Program) toType(raw types.Type) Type {
if t.Empty() { if t.Empty() {
return &aType{p.rtEface(), typ, vkEface} return &aType{p.rtEface(), typ, vkEface}
} }
return &aType{p.rtIface(), typ, vkIface}
case *types.Slice: case *types.Slice:
return &aType{p.rtSlice(), typ, vkSlice} return &aType{p.rtSlice(), typ, vkSlice}
case *types.Map: case *types.Map: