From c19786bdfb80a69dcd37832b1d92e457ed708bed Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 22 May 2024 10:07:21 +0800 Subject: [PATCH] llgo/ssa: AfterInit/SliceLit/InterfaceData, unsafe.Slice; ssa/abi: Basic/Struct --- cl/_testgo/strucintf/in.go | 18 +++++++ cl/_testgo/strucintf/out.ll | 0 cl/compile.go | 4 +- cl/compile_test.go | 2 - cl/internal/foo/foo.go | 9 ++++ internal/abi/type.go | 4 +- internal/runtime/runtime2.go | 21 +++++--- internal/runtime/z_c.go | 4 +- internal/runtime/z_eface.go | 76 +++++++++++++++++++++++++++++ internal/runtime/z_iface.go | 94 ------------------------------------ internal/runtime/z_print.go | 4 +- ssa/{_abi => abi}/abi.go | 52 ++++++++++++++------ ssa/datastruct.go | 36 ++++++++------ ssa/decl.go | 39 ++++++++++----- ssa/expr.go | 36 ++++++++------ ssa/interface.go | 92 +++++++++++++++++++++++++++-------- ssa/package.go | 45 ++++++++++++----- ssa/type.go | 27 +++++++++-- 18 files changed, 361 insertions(+), 202 deletions(-) create mode 100644 cl/_testgo/strucintf/in.go create mode 100644 cl/_testgo/strucintf/out.ll create mode 100644 cl/internal/foo/foo.go create mode 100644 internal/runtime/z_eface.go delete mode 100644 internal/runtime/z_iface.go rename ssa/{_abi => abi}/abi.go (58%) diff --git a/cl/_testgo/strucintf/in.go b/cl/_testgo/strucintf/in.go new file mode 100644 index 00000000..ad3d0140 --- /dev/null +++ b/cl/_testgo/strucintf/in.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/goplus/llgo/_demo/interf/foo" +) + +func main() { + if x, ok := foo.Bar().(struct{ V int }); ok { + println(x.V) + } else { + println("Bar: not ok") + } + if x, ok := foo.F().(struct{ v int }); ok { + println(x.v) + } else { + println("F: not ok") + } +} diff --git a/cl/_testgo/strucintf/out.ll b/cl/_testgo/strucintf/out.ll new file mode 100644 index 00000000..e69de29b diff --git a/cl/compile.go b/cl/compile.go index 6974ee81..d4ce3aa0 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -352,9 +352,9 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do last = len(instrs) - 1 instrs = instrs[:last] } else { - // TODO(xsw): confirm pyMod don't need to call LoadPyModSyms + // TODO(xsw): confirm pyMod don't need to call AfterInit p.inits = append(p.inits, func() { - pkg.PyLoadModSyms(b, ret) + pkg.AfterInit(b, ret) }) } } else if doMainInit { diff --git a/cl/compile_test.go b/cl/compile_test.go index f1841da6..213da252 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -28,11 +28,9 @@ func testCompile(t *testing.T, src, expected string) { cltest.TestCompileEx(t, src, "foo.go", expected) } -/* func TestFromTestgo(t *testing.T) { cltest.FromDir(t, "strucintf", "./_testgo", false) } -*/ func TestFromTestpy(t *testing.T) { cltest.FromDir(t, "", "./_testpy", false) diff --git a/cl/internal/foo/foo.go b/cl/internal/foo/foo.go new file mode 100644 index 00000000..e385a31d --- /dev/null +++ b/cl/internal/foo/foo.go @@ -0,0 +1,9 @@ +package foo + +func Bar() any { + return struct{ V int }{1} +} + +func F() any { + return struct{ v int }{1} +} diff --git a/internal/abi/type.go b/internal/abi/type.go index 085a6475..21f90142 100644 --- a/internal/abi/type.go +++ b/internal/abi/type.go @@ -84,14 +84,12 @@ const ( UnsafePointer ) -/* const ( // TODO (khr, drchase) why aren't these in TFlag? Investigate, fix if possible. KindDirectIface = 1 << 5 KindGCProg = 1 << 6 // Type.gc points to GC program KindMask = (1 << 5) - 1 ) -*/ // TFlag is used by a Type to signal what extra type information is // available in the memory directly following the Type value. @@ -229,7 +227,7 @@ type Imethod struct { Typ TypeOff // .(*FuncType) underneath } -func (t *Type) Kind() Kind { return Kind(t.Kind_) } +func (t *Type) Kind() Kind { return Kind(t.Kind_ & KindMask) } // Size returns the size of data with type t. func (t *Type) Size() uintptr { return t.Size_ } diff --git a/internal/runtime/runtime2.go b/internal/runtime/runtime2.go index d0d8bea7..027c408f 100644 --- a/internal/runtime/runtime2.go +++ b/internal/runtime/runtime2.go @@ -8,21 +8,20 @@ import ( "unsafe" ) -type iface struct { - tab *itab - data unsafe.Pointer -} - -/* type eface struct { _type *_type data unsafe.Pointer } +/* func efaceOf(ep *any) *eface { return (*eface)(unsafe.Pointer(ep)) } -*/ + +type iface struct { + tab *itab + data unsafe.Pointer +} // layout of Itab known to compilers // allocated in non-garbage-collected memory @@ -35,3 +34,11 @@ type itab struct { _ [4]byte fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. } + +func MakeInterface(inter *InterfaceType, typ *Type, data unsafe.Pointer) Interface { + tab := &itab{inter: inter, _type: typ, hash: 0, fun: [1]uintptr{0}} + return Interface{ + tab: tab, data: data, + } +} +*/ diff --git a/internal/runtime/z_c.go b/internal/runtime/z_c.go index 1ea1efbd..2689b8cc 100644 --- a/internal/runtime/z_c.go +++ b/internal/runtime/z_c.go @@ -40,8 +40,8 @@ func Zeroinit(p c.Pointer, size uintptr) c.Pointer { } // TracePanic prints panic message. -func TracePanic(v Interface) { - kind := abi.Kind(v.tab._type.Kind_) +func TracePanic(v Eface) { + kind := abi.Kind(v._type.Kind_) switch { case kind == abi.String: stringTracef(c.Stderr, c.Str("panic: %s\n"), *(*String)(v.data)) diff --git a/internal/runtime/z_eface.go b/internal/runtime/z_eface.go new file mode 100644 index 00000000..03095088 --- /dev/null +++ b/internal/runtime/z_eface.go @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package runtime + +import ( + "unsafe" + + "github.com/goplus/llgo/internal/abi" +) + +type Eface = eface + +// ----------------------------------------------------------------------------- + +func MakeAnyIntptr(typ *Type, data uintptr) Eface { + return eface{ + _type: typ, data: unsafe.Pointer(data), + } +} + +func MakeAnyString(data string) Eface { + typ := basicTypes[abi.String] + return eface{ + _type: typ, data: unsafe.Pointer(&data), + } +} + +func MakeAny(typ *Type, data unsafe.Pointer) Eface { + return eface{ + _type: typ, data: data, + } +} + +func I2Int(v Eface, t *Type) uintptr { + if v._type == t { + return uintptr(v.data) + } + panic("I2Int: type mismatch") +} + +func CheckI2Int(v Eface, t *Type) (uintptr, bool) { + if v._type == t { + return uintptr(v.data), true + } + return 0, false +} + +func I2String(v Eface) string { + if v._type.Kind() == abi.String { + return *(*string)(v.data) + } + panic("I2String: type mismatch") +} + +func CheckI2String(v Eface) (string, bool) { + if v._type.Kind() == abi.String { + return *(*string)(v.data), true + } + return "", false +} + +// ----------------------------------------------------------------------------- diff --git a/internal/runtime/z_iface.go b/internal/runtime/z_iface.go deleted file mode 100644 index c64449ca..00000000 --- a/internal/runtime/z_iface.go +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package runtime - -import ( - "unsafe" - - "github.com/goplus/llgo/internal/abi" -) - -// ----------------------------------------------------------------------------- - -type InterfaceType = abi.InterfaceType - -var ( - TyAny = &InterfaceType{} -) - -// ----------------------------------------------------------------------------- - -type Interface = iface - -func MakeAnyIntptr(typ *Type, data uintptr) Interface { - tab := &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}} - return Interface{ - tab: tab, data: unsafe.Pointer(data), - } -} - -func MakeAnyString(data string) Interface { - typ := basicTypes[abi.String] - tab := &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}} - return Interface{ - tab: tab, data: unsafe.Pointer(&data), - } -} - -func MakeAny(typ *Type, data unsafe.Pointer) Interface { - tab := &itab{inter: TyAny, _type: typ, hash: 0, fun: [1]uintptr{0}} - return Interface{ - tab: tab, data: data, - } -} - -func MakeInterface(inter *InterfaceType, typ *Type, data unsafe.Pointer) Interface { - tab := &itab{inter: inter, _type: typ, hash: 0, fun: [1]uintptr{0}} - return Interface{ - tab: tab, data: data, - } -} - -func I2Int(v Interface, t *Type) uintptr { - if v.tab._type == t { - return uintptr(v.data) - } - panic("I2Int: type mismatch") -} - -func CheckI2Int(v Interface, t *Type) (uintptr, bool) { - if v.tab._type == t { - return uintptr(v.data), true - } - return 0, false -} - -func I2String(v Interface, t *Type) string { - if v.tab._type == t { - return *(*string)(v.data) - } - panic("I2String: type mismatch") -} - -func CheckI2String(v Interface, t *Type) (string, bool) { - if v.tab._type == t { - return *(*string)(v.data), true - } - return "", false -} - -// ----------------------------------------------------------------------------- diff --git a/internal/runtime/z_print.go b/internal/runtime/z_print.go index 4d4ea802..7aeca7f3 100644 --- a/internal/runtime/z_print.go +++ b/internal/runtime/z_print.go @@ -66,6 +66,6 @@ func PrintSlice(s Slice) { print("[", s.len, "/", s.cap, "]", s.data) } -func PrintIface(i Interface) { - print("(", i.tab, ",", i.data, ")") +func PrintEface(e Eface) { + print("(", e._type, ",", e.data, ")") } diff --git a/ssa/_abi/abi.go b/ssa/abi/abi.go similarity index 58% rename from ssa/_abi/abi.go rename to ssa/abi/abi.go index 65578218..3e9fb082 100644 --- a/ssa/_abi/abi.go +++ b/ssa/abi/abi.go @@ -28,46 +28,70 @@ import ( type Builder struct { h hash.Hash buf []byte + Pkg string } // New creates a new ABI type Builder. -func New() *Builder { - h := sha256.New() - buf := make([]byte, sha256.Size) - return &Builder{h, buf} +func New(pkg string) *Builder { + ret := new(Builder) + ret.Init(pkg) + return ret +} + +func (b *Builder) Init(pkg string) { + b.Pkg = pkg + b.h = sha256.New() + b.buf = make([]byte, sha256.Size) } // TypeName returns the ABI type name for the specified type. -func (b *Builder) TypeName(t types.Type) string { +func (b *Builder) TypeName(t types.Type) (ret string, private bool) { switch t := t.(type) { case *types.Basic: - return t.Name() + return BasicName(t), false case *types.Pointer: - return "*" + b.TypeName(t.Elem()) + ret, private = b.TypeName(t.Elem()) + return "*" + ret, private case *types.Struct: return b.StructName(t) } panic("todo") } -// StructName returns the ABI type name for the specified struct type. -func (b *Builder) StructName(t *types.Struct) string { - hash := b.structHash(t) - return "struct$" + base64.RawURLEncoding.EncodeToString(hash) +func BasicName(t *types.Basic) string { + return "_llgo_" + t.Name() } -func (b *Builder) structHash(t *types.Struct) []byte { +// StructName returns the ABI type name for the specified struct type. +func (b *Builder) StructName(t *types.Struct) (ret string, private bool) { + hash, private := b.structHash(t) + hashStr := base64.RawURLEncoding.EncodeToString(hash) + if private { + return b.Pkg + ".struct$" + hashStr, true + } + return "_llgo_struct$" + hashStr, false +} + +func (b *Builder) structHash(t *types.Struct) (ret []byte, private bool) { h := b.h h.Reset() n := t.NumFields() fmt.Fprintln(h, "struct", n) for i := 0; i < n; i++ { f := t.Field(i) + if !f.Exported() { + private = true + } name := f.Name() if f.Embedded() { name = "-" } - fmt.Fprintln(h, name, b.TypeName(f.Type())) + ft, fpriv := b.TypeName(f.Type()) + if fpriv { + private = true + } + fmt.Fprintln(h, name, ft) } - return h.Sum(b.buf[:0]) + ret = h.Sum(b.buf[:0]) + return } diff --git a/ssa/datastruct.go b/ssa/datastruct.go index 0ce70e97..c5ade5d7 100644 --- a/ssa/datastruct.go +++ b/ssa/datastruct.go @@ -70,9 +70,8 @@ func (b Builder) StringData(x Expr) Expr { if debugInstr { log.Printf("StringData %v\n", x.impl) } - prog := b.Prog ptr := llvm.CreateExtractValue(b.impl, x.impl, 0) - return Expr{ptr, prog.CStr()} + return Expr{ptr, b.Prog.CStr()} } // StringLen returns the length of a string. @@ -80,9 +79,8 @@ func (b Builder) StringLen(x Expr) Expr { if debugInstr { log.Printf("StringLen %v\n", x.impl) } - prog := b.Prog ptr := llvm.CreateExtractValue(b.impl, x.impl, 1) - return Expr{ptr, prog.Int()} + return Expr{ptr, b.Prog.Int()} } // SliceData returns the data pointer of a slice. @@ -90,9 +88,8 @@ func (b Builder) SliceData(x Expr) Expr { if debugInstr { log.Printf("SliceData %v\n", x.impl) } - prog := b.Prog ptr := llvm.CreateExtractValue(b.impl, x.impl, 0) - return Expr{ptr, prog.CStr()} + return Expr{ptr, b.Prog.VoidPtr()} } // SliceLen returns the length of a slice. @@ -100,9 +97,8 @@ func (b Builder) SliceLen(x Expr) Expr { if debugInstr { log.Printf("SliceLen %v\n", x.impl) } - prog := b.Prog ptr := llvm.CreateExtractValue(b.impl, x.impl, 1) - return Expr{ptr, prog.Int()} + return Expr{ptr, b.Prog.Int()} } // SliceCap returns the length of a slice cap. @@ -110,9 +106,8 @@ func (b Builder) SliceCap(x Expr) Expr { if debugInstr { log.Printf("SliceCap %v\n", x.impl) } - prog := b.Prog ptr := llvm.CreateExtractValue(b.impl, x.impl, 2) - return Expr{ptr, prog.Int()} + return Expr{ptr, b.Prog.Int()} } // The IndexAddr instruction yields the address of the element at @@ -188,7 +183,7 @@ func (b Builder) Index(x, idx Expr, addr func(Expr) Expr) Expr { if addr != nil { ptr = addr(x) } else { - size := b.SizeOf(telem, t.Len()) + size := SizeOf(prog, telem, t.Len()) ptr = b.Alloca(size) b.Store(ptr, x) } @@ -255,7 +250,7 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) { ret.impl = b.InlineCall(b.Pkg.rtFunc("NewStringSlice"), x, low, high).impl return case *types.Slice: - nEltSize = b.SizeOf(prog.Index(x.Type)) + nEltSize = SizeOf(prog, prog.Index(x.Type)) nCap = b.SliceCap(x) if high.IsNil() { high = b.SliceCap(x) @@ -268,7 +263,7 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) { case *types.Array: elem := prog.rawType(te.Elem()) ret.Type = prog.Slice(elem) - nEltSize = b.SizeOf(elem) + nEltSize = SizeOf(prog, elem) nCap = prog.IntVal(uint64(te.Len()), prog.Int()) if high.IsNil() { high = nCap @@ -283,6 +278,18 @@ func (b Builder) Slice(x, low, high, max Expr) (ret Expr) { return } +// SliceLit creates a new slice with the specified elements. +func (b Builder) SliceLit(t Type, elts ...Expr) Expr { + prog := b.Prog + telem := prog.Index(t) + ptr := b.AllocU(telem, int64(len(elts))) + for i, elt := range elts { + b.Store(b.Advance(ptr, prog.Val(i)), elt) + } + size := llvm.ConstInt(prog.tyInt(), uint64(len(elts)), false) + return b.unsafeSlice(ptr, size, size) +} + // ----------------------------------------------------------------------------- // The MakeMap instruction creates a new hash-table-based map object @@ -323,10 +330,11 @@ func (b Builder) MakeSlice(t Type, len, cap Expr) (ret Expr) { log.Printf("MakeSlice %v, %v, %v\n", t.RawType(), len.impl, cap.impl) } pkg := b.Pkg + prog := b.Prog if cap.IsNil() { cap = len } - elemSize := b.SizeOf(b.Prog.Index(t)) + elemSize := SizeOf(prog, prog.Index(t)) size := b.BinOp(token.MUL, cap, elemSize) ptr := b.InlineCall(pkg.rtFunc("AllocZ"), size) ret.impl = b.InlineCall(pkg.rtFunc("NewSlice"), ptr, len, cap).impl diff --git a/ssa/decl.go b/ssa/decl.go index 97856aa3..f859563f 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -58,6 +58,13 @@ type aNamedConst struct { // it augments with the name and position of its 'const' declaration. type NamedConst = *aNamedConst +/* +// NewConst creates a new named constant. +func (p Package) NewConst(name string, val constant.Value) NamedConst { + return &aNamedConst{} +} +*/ + // ----------------------------------------------------------------------------- type aGlobal struct { @@ -75,9 +82,21 @@ func (p Package) NewVar(name string, typ types.Type, bg Background) Global { return v } t := p.Prog.Type(typ, bg) + return p.doNewVar(name, t) +} + +// NewVarFrom creates a new global variable. +func (p Package) NewVarFrom(name string, t Type) Global { + if v, ok := p.vars[name]; ok { + return v + } + return p.doNewVar(name, t) +} + +func (p Package) doNewVar(name string, t Type) Global { var gbl llvm.Value var array bool - if t.kind == vkPtr && p.Prog.Elem(t).kind == vkArray { + if t.kind == vkPtr && p.Prog.Elem(t).kind == vkArray { // TODO(xsw): check this code typ := p.Prog.Elem(t).ll gbl = llvm.AddGlobal(p.mod, typ, name) gbl.SetInitializer(llvm.Undef(typ)) @@ -97,7 +116,7 @@ func (p Package) VarOf(name string) Global { // Init initializes the global variable with the given value. func (g Global) Init(v Expr) { - if g.array && v.kind == vkPtr { + if g.array && v.kind == vkPtr { // TODO(xsw): check this code return } g.impl.SetInitializer(v.impl) @@ -350,15 +369,14 @@ func (p Package) PyObjOf(name string) PyObjRef { return p.pyobjs[name] } -// PyLoadModSyms loads module symbols used in this package. -func (p Package) PyLoadModSyms(b Builder, ret BasicBlock) { - objs := p.pyobjs - n := len(objs) - if n == 0 { - return - } +func (p Package) pyHasModSyms() bool { + return len(p.pyobjs) > 0 +} - names := make([]string, 0, n) +// pyLoadModSyms loads module symbols used in this package. +func (p Package) pyLoadModSyms(b Builder) { + objs := p.pyobjs + names := make([]string, 0, len(objs)) for name := range objs { names = append(names, name) } @@ -376,7 +394,6 @@ func (p Package) PyLoadModSyms(b Builder, ret BasicBlock) { } } - b.SetBlockEx(ret, afterInit) for _, modName := range modNames { objs := mods[modName] b.PyLoadModSyms(modName, objs...) diff --git a/ssa/expr.go b/ssa/expr.go index 49ccc6d9..5a25085b 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -180,13 +180,6 @@ func (b Builder) Const(v constant.Value, typ Type) Expr { panic(fmt.Sprintf("unsupported Const: %v, %v", v, raw)) } -// SizeOf returns the size of a type. -func (b Builder) SizeOf(t Type, n ...int64) Expr { - prog := b.Prog - size := prog.SizeOf(t, n...) - return prog.IntVal(size, prog.Uintptr()) -} - // CStr returns a c-style string constant expression. func (b Builder) CStr(v string) Expr { return Expr{llvm.CreateGlobalStringPtr(b.impl, v), b.Prog.CStr()} @@ -200,10 +193,17 @@ func (b Builder) Str(v string) (ret Expr) { return Expr{aggregateValue(b.impl, prog.rtString(), data, size), prog.String()} } -// unsafe.String(data *byte, size int) string -func (b Builder) String(data, size Expr) Expr { +// unsafeString(data *byte, size int) string +func (b Builder) unsafeString(data, size llvm.Value) Expr { prog := b.Prog - return Expr{aggregateValue(b.impl, prog.rtString(), data.impl, size.impl), prog.String()} + return Expr{aggregateValue(b.impl, prog.rtString(), data, size), prog.String()} +} + +// unsafeSlice(data *T, size, cap int) []T +func (b Builder) unsafeSlice(data Expr, size, cap llvm.Value) Expr { + prog := b.Prog + tslice := prog.Slice(prog.Elem(data.Type)) + return Expr{aggregateValue(b.impl, prog.rtSlice(), data.impl, size, cap), tslice} } // ----------------------------------------------------------------------------- @@ -667,7 +667,7 @@ func (b Builder) Alloc(elem Type, heap bool) (ret Expr) { } prog := b.Prog pkg := b.Pkg - size := b.SizeOf(elem) + size := SizeOf(prog, elem) if heap { ret = b.InlineCall(pkg.rtFunc("AllocZ"), size) } else { @@ -683,9 +683,10 @@ func (b Builder) AllocU(elem Type, n ...int64) (ret Expr) { if debugInstr { log.Printf("AllocU %v, %v\n", elem.raw.Type, n) } - size := b.SizeOf(elem, n...) + prog := b.Prog + size := SizeOf(prog, elem, n...) ret = b.InlineCall(b.Pkg.rtFunc("AllocU"), size) - ret.Type = b.Prog.Pointer(elem) + ret.Type = prog.Pointer(elem) return } @@ -1035,8 +1036,8 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { typ = prog.VoidPtr() case vkString: fn = "PrintString" - case vkInterface: - fn = "PrintIface" + case vkEface: + fn = "PrintEface" // case vkComplex: // fn = "PrintComplex" default: @@ -1067,7 +1068,10 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { } } case "String": // unsafe.String - return b.String(args[0], args[1]) + return b.unsafeString(args[0].impl, args[1].impl) + case "Slice": // unsafe.Slice + size := args[1].impl + return b.unsafeSlice(args[0], size, size) } panic("todo: " + fn) } diff --git a/ssa/interface.go b/ssa/interface.go index 16004054..84f7fe04 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -21,31 +21,72 @@ import ( "go/types" "log" - "github.com/goplus/llgo/internal/abi" + "github.com/goplus/llgo/ssa/abi" "github.com/goplus/llvm" ) // ----------------------------------------------------------------------------- // abiBasic returns the abi type of the specified basic kind. -func (b Builder) abiBasic(kind types.BasicKind) Expr { - return b.InlineCall(b.Pkg.rtFunc("Basic"), b.Prog.Val(int(kind))) +func (b Builder) abiBasic(t *types.Basic) Expr { + name := abi.BasicName(t) + g := b.Pkg.NewVarFrom(name, b.Prog.AbiTypePtrPtr()) + return g.Expr } -/* // abiStruct returns the abi type of the specified struct type. func (b Builder) abiStruct(t *types.Struct) Expr { - // name := "__llgo_" + b.Prog.abi.StructName(t) + pkg := b.Pkg + name, _ := pkg.abi.StructName(t) + if v := pkg.VarOf(name); v != nil { + return v.Expr + } + prog := b.Prog + g := pkg.doNewVar(name, prog.AbiTypePtrPtr()) + g.Init(prog.Null(g.Type)) + pkg.abitys = append(pkg.abitys, func() { + b.structOf(t) + }) + return g.Expr } -*/ -// AbiType returns the abi type of the specified type. -func (b Builder) AbiType(t Type) Expr { - switch tx := t.raw.Type.(type) { +// func Struct(size uintptr, pkgPath string, fields []abi.StructField) *abi.Type +func (b Builder) structOf(t *types.Struct) Expr { + pkg := b.Pkg + prog := b.Prog + n := t.NumFields() + flds := make([]Expr, n) + strucAbi := pkg.rtFunc("Struct") + sfAbi := pkg.rtFunc("StructField") + typ := prog.rawType(t) + for i := 0; i < n; i++ { + f := t.Field(i) + off := uintptr(prog.OffsetOf(typ, i)) + flds[i] = b.structField(sfAbi, prog, f, off, t.Tag(i)) + } + pkgPath := prog.Val(pkg.abi.Pkg) + params := strucAbi.raw.Type.(*types.Signature).Params() + tSlice := prog.rawType(params.At(params.Len() - 1).Type().(*types.Slice)) + fldSlice := b.SliceLit(tSlice, flds...) + return b.Call(strucAbi, pkgPath, fldSlice) +} + +// func StructField(name string, typ *abi.Type, off uintptr, tag string, exported, embedded bool) abi.StructField +func (b Builder) structField(sfAbi Expr, prog Program, f *types.Var, offset uintptr, tag string) Expr { + name := prog.Val(f.Name()) + typ := b.abiType(f.Type()) + exported := prog.Val(f.Exported()) + embedded := prog.Val(f.Embedded()) + return b.Call(sfAbi, name, typ, prog.Val(offset), prog.Val(tag), exported, embedded) +} + +// abiType returns the abi type of the specified type. +func (b Builder) abiType(raw types.Type) Expr { + switch tx := raw.(type) { case *types.Basic: - return b.abiBasic(tx.Kind()) - //case *types.Struct: - // return b.abiStruct(tx) + return b.abiBasic(tx) + case *types.Struct: + return b.abiStruct(tx) } panic("todo") } @@ -94,14 +135,14 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { case kind == types.String: return Expr{b.InlineCall(b.Pkg.rtFunc("MakeAnyString"), x).impl, tinter} } - /* case *types.Struct: + case *types.Struct: size := int(prog.SizeOf(typ)) if size > prog.PointerSize() { return b.makeIntfAlloc(tinter, rawIntf, typ, x) } tv := prog.ctx.IntType(size * 8) iv := llvm.CreateBitCast(b.impl, x.impl, tv) - return b.makeIntfByIntptr(tinter, rawIntf, typ, iv) */ + return b.makeIntfByIntptr(tinter, rawIntf, typ, iv) } panic("todo") } @@ -114,7 +155,7 @@ func (b Builder) makeIntfAlloc(tinter Type, rawIntf *types.Interface, typ Type, func (b Builder) makeIntfByPtr(tinter Type, rawIntf *types.Interface, typ Type, vptr Expr) (ret Expr) { if rawIntf.Empty() { - ret = b.InlineCall(b.Pkg.rtFunc("MakeAny"), b.AbiType(typ), vptr) + ret = b.InlineCall(b.Pkg.rtFunc("MakeAny"), b.abiType(typ.raw.Type), vptr) ret.Type = tinter return } @@ -125,7 +166,7 @@ func (b Builder) makeIntfByIntptr(tinter Type, rawIntf *types.Interface, typ Typ if rawIntf.Empty() { tptr := b.Prog.Uintptr() x = llvm.CreateIntCast(b.impl, x, tptr.ll) - impl := b.InlineCall(b.Pkg.rtFunc("MakeAnyIntptr"), b.AbiType(typ), Expr{x, tptr}).impl + impl := b.InlineCall(b.Pkg.rtFunc("MakeAnyIntptr"), b.abiType(typ.raw.Type), Expr{x, tptr}).impl return Expr{impl, tinter} } panic("todo") @@ -183,13 +224,14 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { } fn := pkg.rtFunc(fnName) var kind types.BasicKind + var typ Expr switch t := assertedTyp.raw.Type.(type) { case *types.Basic: kind = t.Kind() + typ = b.abiBasic(t) default: panic("todo") } - typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(kind))) ret = b.InlineCall(fn, x, typ) if kind != types.Uintptr { conv := func(v llvm.Value) llvm.Value { @@ -226,11 +268,21 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) (ret Expr) { if commaOk { fnName = "CheckI2String" } - fn := pkg.rtFunc(fnName) - typ := b.InlineCall(pkg.rtFunc("Basic"), b.Prog.Val(int(abi.String))) - return b.InlineCall(fn, x, typ) + return b.InlineCall(pkg.rtFunc(fnName), x) + case vkStruct: } panic("todo") } // ----------------------------------------------------------------------------- + +// InterfaceData returns the data pointer of an interface. +func (b Builder) InterfaceData(x Expr) Expr { + if debugInstr { + log.Printf("InterfaceData %v\n", x.impl) + } + ptr := llvm.CreateExtractValue(b.impl, x.impl, 1) + return Expr{ptr, b.Prog.VoidPtr()} +} + +// ----------------------------------------------------------------------------- diff --git a/ssa/package.go b/ssa/package.go index 9e9356bb..e5960391 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -20,6 +20,7 @@ import ( "go/token" "go/types" + "github.com/goplus/llgo/ssa/abi" "github.com/goplus/llvm" "golang.org/x/tools/go/types/typeutil" ) @@ -99,7 +100,6 @@ type aProgram struct { ctx llvm.Context typs typeutil.Map // rawType -> Type gocvt goTypes - //abi *abi.Builder rt *types.Package rtget func() *types.Package @@ -144,6 +144,7 @@ type aProgram struct { u64Ty Type pyObjPtr Type pyObjPPtr Type + abiTyptr Type pyImpTy *types.Signature pyNewList *types.Signature @@ -184,7 +185,7 @@ func NewProgram(target *Target) Program { */ is32Bits := (td.PointerSize() == 4 || target.GOARCH == "x86") // TODO(xsw): remove temp code return &aProgram{ - ctx: ctx, gocvt: newGoTypes(), // abi: abi.New(), + ctx: ctx, gocvt: newGoTypes(), target: target, td: td, is32Bits: is32Bits, named: make(map[string]llvm.Type), } @@ -243,9 +244,9 @@ func (p Program) rtType(name string) Type { return p.rawType(p.rtNamed(name)) } -func (p Program) rtIface() llvm.Type { +func (p Program) rtEface() llvm.Type { if p.rtIfaceTy.IsNil() { - p.rtIfaceTy = p.rtType("Interface").ll + p.rtIfaceTy = p.rtType("Eface").ll } return p.rtIfaceTy } @@ -284,7 +285,23 @@ func (p Program) NewPackage(name, pkgPath string) Package { p.NeedRuntime = false // Don't need reset p.needPyInit here // p.needPyInit = false - return &aPackage{mod, gbls, fns, stubs, pyobjs, pymods, p} + ret := &aPackage{ + mod: mod, vars: gbls, fns: fns, stubs: stubs, + pyobjs: pyobjs, pymods: pymods, Prog: p} + return ret +} + +// AbiTypePtr returns *abi.Type. +func (p Program) AbiTypePtr() Type { + if p.abiTyptr == nil { + p.abiTyptr = p.rawType(types.NewPointer(p.rtNamed("Type"))) + } + return p.abiTyptr +} + +// AbiTypePtr returns **abi.Type. +func (p Program) AbiTypePtrPtr() Type { + return p.Pointer(p.AbiTypePtr()) } // PyObjectPtrPtr returns the **py.Object type. @@ -440,6 +457,8 @@ func (p Program) Uint64() Type { // and unspecified other things too. type aPackage struct { mod llvm.Module + abi abi.Builder + abitys []func() vars map[string]Global fns map[string]Function stubs map[string]Function @@ -450,13 +469,6 @@ type aPackage struct { type Package = *aPackage -/* -// NewConst creates a new named constant. -func (p Package) NewConst(name string, val constant.Value) NamedConst { - return &aNamedConst{} -} -*/ - func (p Package) rtFunc(fnName string) Expr { fn := p.Prog.runtime().Scope().Lookup(fnName).(*types.Func) name := FullName(fn.Pkg(), fnName) @@ -509,6 +521,15 @@ func (p Package) String() string { return p.mod.String() } +// AfterInit is called after the package is initialized (init all packages that depends on). +func (p Package) AfterInit(b Builder, ret BasicBlock) { + doAfterInit := p.pyHasModSyms() + if doAfterInit { + b.SetBlockEx(ret, afterInit) + p.pyLoadModSyms(b) + } +} + /* type CodeGenFileType = llvm.CodeGenFileType diff --git a/ssa/type.go b/ssa/type.go index b7307c39..391e95da 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -49,7 +49,9 @@ const ( vkSlice vkArray vkMap - vkInterface + vkEface + vkIface + vkStruct vkPhisExpr = -1 ) @@ -99,6 +101,22 @@ func (p Program) SizeOf(typ Type, n ...int64) uint64 { return size } +// OffsetOf returns the offset of a field in a struct. +func (p Program) OffsetOf(typ Type, i int) uint64 { + return p.td.ElementOffset(typ.ll, i) +} + +// SizeOf returns the size of a type. +func SizeOf(prog Program, t Type, n ...int64) Expr { + size := prog.SizeOf(t, n...) + return prog.IntVal(size, prog.Uintptr()) +} + +func OffsetOf(prog Program, t Type, i int) Expr { + offset := prog.OffsetOf(t, i) + return prog.IntVal(offset, prog.Uintptr()) +} + func (p Program) PointerSize() int { return p.td.PointerSize() } @@ -113,7 +131,6 @@ func (p Program) Pointer(typ Type) Type { func (p Program) Elem(typ Type) Type { elem := typ.raw.Type.(interface { - types.Type Elem() types.Type }).Elem() return p.rawType(elem) @@ -247,7 +264,9 @@ func (p Program) toType(raw types.Type) Type { elem := p.rawType(t.Elem()) return &aType{llvm.PointerType(elem.ll, 0), typ, vkPtr} case *types.Interface: - return &aType{p.rtIface(), typ, vkInterface} + if t.Empty() { + return &aType{p.rtEface(), typ, vkEface} + } case *types.Slice: return &aType{p.rtSlice(), typ, vkSlice} case *types.Map: @@ -283,6 +302,8 @@ func (p Program) toLLVMStruct(raw *types.Struct) (ret llvm.Type, kind valueKind) ret = p.ctx.StructType(fields, false) if isClosure(raw) { kind = vkClosure + } else { + kind = vkStruct } return }