diff --git a/cl/compile.go b/cl/compile.go index 7105730d..00f63ee9 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -558,6 +558,12 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue switch v := iv.(type) { case *ssa.Call: 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) if kind == fnIgnore { return diff --git a/internal/runtime/z_face.go b/internal/runtime/z_face.go index c7611fd3..149c06c6 100644 --- a/internal/runtime/z_face.go +++ b/internal/runtime/z_face.go @@ -43,6 +43,8 @@ type ( Itab = itab ) +type Imethod = abi.Imethod +type Method = abi.Method type FuncType = abi.FuncType type InterfaceType = abi.InterfaceType @@ -54,14 +56,105 @@ func ToEface(i Iface) Eface { // ----------------------------------------------------------------------------- const ( - typeHdrSize = unsafe.Sizeof(abi.Type{}) - funcTypeHdrSize = unsafe.Sizeof(abi.FuncType{}) - uncommonTypeHdrSize = unsafe.Sizeof(abi.UncommonType{}) - methodSize = unsafe.Sizeof(abi.Method{}) - pointerSize = unsafe.Sizeof(uintptr(0)) - itabHdrSize = unsafe.Sizeof(itab{}) - pointerSize + typeHdrSize = unsafe.Sizeof(abi.Type{}) + arrayTypeHdrSize = unsafe.Sizeof(abi.ArrayType{}) + chanTypeHdrSize = unsafe.Sizeof(abi.ChanType{}) + 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{}) + methodSize = unsafe.Sizeof(abi.Method{}) + pointerSize = unsafe.Sizeof(uintptr(0)) + 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 Func(in, out []*Type, variadic bool) *FuncType { n := len(in) + len(out) @@ -85,61 +178,8 @@ func Func(in, out []*Type, variadic bool) *FuncType { 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. -func Interface(pkgPath string, methods []abi.Imethod) *Type { +func Interface(pkgPath string, methods []abi.Imethod) *InterfaceType { ret := &abi.InterfaceType{ Type: Type{ Size_: unsafe.Sizeof(eface{}), @@ -149,7 +189,7 @@ func Interface(pkgPath string, methods []abi.Imethod) *Type { PkgPath_: pkgPath, Methods: methods, } - return &ret.Type + return ret } // NewItab returns a new itab. @@ -158,7 +198,7 @@ func NewItab(inter *InterfaceType, typ *Type) *Itab { size := itabHdrSize + uintptr(n)*pointerSize ptr := AllocU(size) - ret := (*Itab)(ptr) + ret := (*itab)(ptr) ret.inter = inter ret._type = typ ret.hash = typ.Hash diff --git a/internal/runtime/z_type.go b/internal/runtime/z_type.go index d39ad954..3693a8f4 100644 --- a/internal/runtime/z_type.go +++ b/internal/runtime/z_type.go @@ -112,17 +112,22 @@ func Struct(pkgPath string, size uintptr, fields ...abi.StructField) *Type { // ----------------------------------------------------------------------------- -// Pointer returns a pointer type. -func Pointer(elem *Type) *Type { - ret := &abi.PtrType{ - Type: Type{ - Size_: unsafe.Sizeof(uintptr(0)), - Hash: uint32(abi.Pointer), // TODO(xsw): hash - Kind_: uint8(abi.Pointer), - }, - Elem: elem, +// PointerTo returns the pointer type with element elem. +func PointerTo(elem *Type) *Type { + ret := elem.PtrToThis_ + if ret == nil { + ptr := &abi.PtrType{ + Type: Type{ + Size_: unsafe.Sizeof(uintptr(0)), + Hash: uint32(abi.Pointer), // TODO(xsw): hash + Kind_: uint8(abi.Pointer), + }, + Elem: elem, + } + ret = &ptr.Type + elem.PtrToThis_ = ret } - return &ret.Type + return ret } // ----------------------------------------------------------------------------- diff --git a/ssa/cl_test.go b/ssa/cl_test.go index 513944c1..488a36bc 100644 --- a/ssa/cl_test.go +++ b/ssa/cl_test.go @@ -26,7 +26,7 @@ import ( ) func TestFromTestgo(t *testing.T) { - cltest.FromDir(t, "", "../cl/_testgo", false) + cltest.FromDir(t, "errors", "../cl/_testgo", false) } func TestFromTestpy(t *testing.T) { diff --git a/ssa/expr.go b/ssa/expr.go index 0a4d47ea..436d40fa 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -193,12 +193,6 @@ func (b Builder) Str(v string) (ret Expr) { 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 func (b Builder) unsafeString(data, size llvm.Value) Expr { prog := b.Prog @@ -906,7 +900,16 @@ func (b Builder) InlineCall(fn Expr, args ...Expr) (ret Expr) { 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 // 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) // t4 = t3() -// t7 = invoke t5.Println(...t6) func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { if debugInstr { var b bytes.Buffer @@ -954,7 +956,7 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { sig = raw.(*types.Signature) ll = fn.ll default: - panic("unreachable") + log.Panicf("unreachable: %d(%T)\n", kind, raw) } ret.Type = prog.retType(sig) ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b)) diff --git a/ssa/interface.go b/ssa/interface.go index 217d231a..52b4669b 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -55,23 +55,74 @@ func (b Builder) abiTypeOf(t types.Type) Expr { return b.abiStructOf(t) case *types.Named: return b.abiNamedOf(t) + case *types.Interface: + return b.abiInterfaceOf(t) + case *types.Signature: + return b.abiFuncOf(t) } 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 (b Builder) abiNamedOf(t *types.Named) Expr { under := b.abiTypeOf(t.Underlying()) + path := abi.PathOf(t.Obj().Pkg()) name := NameOf(t) prog := b.Prog pkg := b.Pkg - pkgPath := b.pkgName(pkg.Path()) fn := pkg.rtFunc("Named") tSlice := lastParamType(prog, fn) // TODO(xsw): methods 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 { @@ -93,7 +144,7 @@ func (b Builder) abiStructOf(t *types.Struct) Expr { off := uintptr(prog.OffsetOf(typ, 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) fldSlice := b.SliceLit(tSlice, flds...) 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) } +// 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 @@ -185,7 +255,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { case abi.Indirect: vptr := b.AllocU(typ) 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 if lvl > 0 { @@ -194,7 +264,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { var u llvm.Value switch kind { case abi.Pointer: - return Expr{b.unsafeEface(tabi.impl, ximpl), tinter} + return Expr{b.unsafeInterface(rawIntf, tabi, ximpl), tinter} case abi.Integer: tu := prog.Uintptr() u = llvm.CreateIntCast(b.impl, ximpl, tu.ll) @@ -205,7 +275,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { panic("todo") } 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 { diff --git a/ssa/package.go b/ssa/package.go index 5df247db..c7347a7f 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -125,6 +125,7 @@ type aProgram struct { rtStringTy llvm.Type rtEfaceTy llvm.Type + rtIfaceTy llvm.Type rtSliceTy llvm.Type rtMapTy llvm.Type @@ -149,7 +150,6 @@ type aProgram struct { pyObjPPtr Type abiTyptr Type abiTypptr Type - //efaceTy Type pyImpTy *types.Signature pyNewList *types.Signature @@ -256,6 +256,13 @@ func (p Program) rtEface() llvm.Type { 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 { if p.rtMapTy.IsNil() { p.rtMapTy = p.rtType("Map").ll diff --git a/ssa/type.go b/ssa/type.go index 2b1f3608..6f8b2161 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -274,6 +274,7 @@ func (p Program) toType(raw types.Type) Type { if t.Empty() { return &aType{p.rtEface(), typ, vkEface} } + return &aType{p.rtIface(), typ, vkIface} case *types.Slice: return &aType{p.rtSlice(), typ, vkSlice} case *types.Map: