From 0676680e5ba6feab130f95d5d192ecad02c89c88 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 15:14:19 +0800 Subject: [PATCH 01/37] wip: add metadata package summary model --- internal/metadata/builder.go | 84 ++++++ internal/metadata/decode.go | 314 ++++++++++++++++++++ internal/metadata/encode.go | 137 +++++++++ internal/metadata/format.go | 141 +++++++++ internal/metadata/global_summary.go | 357 +++++++++++++++++++++++ internal/metadata/global_summary_test.go | 158 ++++++++++ internal/metadata/meta.go | 181 ++++++++++++ internal/metadata/metadata_test.go | 220 ++++++++++++++ 8 files changed, 1592 insertions(+) create mode 100644 internal/metadata/builder.go create mode 100644 internal/metadata/decode.go create mode 100644 internal/metadata/encode.go create mode 100644 internal/metadata/format.go create mode 100644 internal/metadata/global_summary.go create mode 100644 internal/metadata/global_summary_test.go create mode 100644 internal/metadata/meta.go create mode 100644 internal/metadata/metadata_test.go diff --git a/internal/metadata/builder.go b/internal/metadata/builder.go new file mode 100644 index 0000000000..74ca47d96f --- /dev/null +++ b/internal/metadata/builder.go @@ -0,0 +1,84 @@ +package metadata + +// Builder accumulates per-package metadata facts and builds a PackageMeta. +type Builder struct { + pm *PackageMeta + strToID map[string]uint32 +} + +// NewBuilder creates an empty metadata builder. +func NewBuilder() *Builder { + return &Builder{ + pm: NewPackageMeta(nil), + strToID: make(map[string]uint32), + } +} + +// Symbol registers a module-level named symbol. +func (b *Builder) Symbol(s string) Symbol { + return Symbol(b.intern(s)) +} + +// Name registers a plain semantic name. +func (b *Builder) Name(s string) Name { + return Name(b.intern(s)) +} + +func (b *Builder) intern(s string) uint32 { + if id, ok := b.strToID[s]; ok { + return id + } + id := uint32(len(b.strToID)) + b.strToID[s] = id + return id +} + +// AddEdge records an ordinary reachability edge src -> dst. +func (b *Builder) AddEdge(src, dst Symbol) { + b.pm.ordinaryEdges[src] = append(b.pm.ordinaryEdges[src], dst) +} + +// AddTypeChild records that parent type references child type. +func (b *Builder) AddTypeChild(parent, child Symbol) { + b.pm.typeChildren[parent] = append(b.pm.typeChildren[parent], child) +} + +// AddIfaceEntry records the method set of an interface type. +func (b *Builder) AddIfaceEntry(iface Symbol, methods []MethodSig) { + b.pm.interfaceInfo[iface] = append(b.pm.interfaceInfo[iface], methods...) +} + +// AddUseIface records types converted to interface when owner is reachable. +func (b *Builder) AddUseIface(owner Symbol, types []Symbol) { + b.pm.useIface[owner] = append(b.pm.useIface[owner], types...) +} + +// AddUseIfaceMethod records interface method calls when owner is reachable. +func (b *Builder) AddUseIfaceMethod(owner Symbol, demands []IfaceMethodDemand) { + b.pm.useIfaceMethod[owner] = append(b.pm.useIfaceMethod[owner], demands...) +} + +// AddMethodInfo records concrete type method table slots. +func (b *Builder) AddMethodInfo(typeID Symbol, slots []MethodSlot) { + b.pm.methodInfo[typeID] = append(b.pm.methodInfo[typeID], slots...) +} + +// AddUseNamedMethod records constant MethodByName method names. +func (b *Builder) AddUseNamedMethod(owner Symbol, names []Name) { + b.pm.useNamedMethod[owner] = append(b.pm.useNamedMethod[owner], names...) +} + +// AddReflectMethod records that owner triggers conservative reflection handling. +func (b *Builder) AddReflectMethod(owner Symbol) { + b.pm.reflectMethod[owner] = struct{}{} +} + +// Build finalizes the builder and returns the package metadata. +func (b *Builder) Build() *PackageMeta { + table := make([]string, len(b.strToID)) + for s, id := range b.strToID { + table[id] = s + } + b.pm.stringTable = table + return b.pm +} diff --git a/internal/metadata/decode.go b/internal/metadata/decode.go new file mode 100644 index 0000000000..d82f49f925 --- /dev/null +++ b/internal/metadata/decode.go @@ -0,0 +1,314 @@ +package metadata + +import ( + "encoding/binary" + "fmt" + "io" + "math" +) + +// ReadMeta deserializes a PackageMeta from the LLPS v1 binary format. +func ReadMeta(r io.Reader) (*PackageMeta, error) { + data, err := io.ReadAll(r) + if err != nil { + return nil, fmt.Errorf("read meta: %w", err) + } + return decodeMeta(data) +} + +func decodeMeta(data []byte) (*PackageMeta, error) { + pos := 0 + + if len(data) < len(magicV1)+1 { + return nil, fmt.Errorf("data too short for header") + } + if magic := string(data[:len(magicV1)]); magic != magicV1 { + return nil, fmt.Errorf("bad magic: %q", magic) + } + pos += len(magicV1) + + version, err := readUvarint(data, &pos) + if err != nil { + return nil, fmt.Errorf("decode version: %w", err) + } + if version != version1 { + return nil, fmt.Errorf("unsupported version: %d", version) + } + + table, err := decodeStringTable(data, &pos) + if err != nil { + return nil, fmt.Errorf("stringTable: %w", err) + } + meta := NewPackageMeta(table) + + if err := decodeSymbolMap(data, &pos, meta.ordinaryEdges); err != nil { + return nil, fmt.Errorf("ordinaryEdges: %w", err) + } + if err := decodeSymbolMap(data, &pos, meta.typeChildren); err != nil { + return nil, fmt.Errorf("typeChildren: %w", err) + } + if err := decodeInterfaceInfo(data, &pos, meta.interfaceInfo); err != nil { + return nil, fmt.Errorf("interfaceInfo: %w", err) + } + if err := decodeSymbolMap(data, &pos, meta.useIface); err != nil { + return nil, fmt.Errorf("useIface: %w", err) + } + if err := decodeUseIfaceMethod(data, &pos, meta.useIfaceMethod); err != nil { + return nil, fmt.Errorf("useIfaceMethod: %w", err) + } + if err := decodeMethodInfo(data, &pos, meta.methodInfo); err != nil { + return nil, fmt.Errorf("methodInfo: %w", err) + } + if err := decodeUseNamedMethod(data, &pos, meta.useNamedMethod); err != nil { + return nil, fmt.Errorf("useNamedMethod: %w", err) + } + if err := decodeReflectMethod(data, &pos, meta.reflectMethod); err != nil { + return nil, fmt.Errorf("reflectMethod: %w", err) + } + if pos != len(data) { + return nil, fmt.Errorf("trailing data at pos %d", pos) + } + return meta, nil +} + +func decodeStringTable(data []byte, pos *int) ([]string, error) { + count, err := readUvarint(data, pos) + if err != nil { + return nil, fmt.Errorf("decode count: %w", err) + } + if count > uint64(math.MaxInt) { + return nil, fmt.Errorf("count overflows int: %d", count) + } + table := make([]string, int(count)) + for i := range table { + size, err := readUvarint(data, pos) + if err != nil { + return nil, fmt.Errorf("decode string %d len: %w", i, err) + } + if size > uint64(len(data)-*pos) { + return nil, fmt.Errorf("string %d out of range", i) + } + table[i] = string(data[*pos : *pos+int(size)]) + *pos += int(size) + } + return table, nil +} + +func readUvarint(data []byte, pos *int) (uint64, error) { + if *pos >= len(data) { + return 0, io.ErrUnexpectedEOF + } + value, n := binary.Uvarint(data[*pos:]) + if n == 0 { + return 0, io.ErrUnexpectedEOF + } + if n < 0 { + return 0, fmt.Errorf("uvarint overflow at pos %d", *pos) + } + *pos += n + return value, nil +} + +func readSymbol(data []byte, pos *int) (Symbol, error) { + value, err := readUvarint(data, pos) + if err != nil { + return 0, err + } + if value > math.MaxUint32 { + return 0, fmt.Errorf("symbol overflows uint32: %d", value) + } + return Symbol(value), nil +} + +func readName(data []byte, pos *int) (Name, error) { + value, err := readUvarint(data, pos) + if err != nil { + return 0, err + } + if value > math.MaxUint32 { + return 0, fmt.Errorf("name overflows uint32: %d", value) + } + return Name(value), nil +} + +func readCount(data []byte, pos *int, label string) (int, error) { + count, err := readUvarint(data, pos) + if err != nil { + return 0, err + } + if count > uint64(math.MaxInt) { + return 0, fmt.Errorf("%s count overflows int: %d", label, count) + } + return int(count), nil +} + +func decodeSymbolMap(data []byte, pos *int, m map[Symbol][]Symbol) error { + count, err := readCount(data, pos, "symbol map") + if err != nil { + return err + } + for range count { + key, err := readSymbol(data, pos) + if err != nil { + return err + } + nvalues, err := readCount(data, pos, "symbol values") + if err != nil { + return err + } + values := make([]Symbol, nvalues) + for i := range values { + values[i], err = readSymbol(data, pos) + if err != nil { + return err + } + } + m[key] = values + } + return nil +} + +func decodeInterfaceInfo(data []byte, pos *int, m map[Symbol][]MethodSig) error { + count, err := readCount(data, pos, "interface info") + if err != nil { + return err + } + for range count { + iface, err := readSymbol(data, pos) + if err != nil { + return err + } + nmethods, err := readCount(data, pos, "interface methods") + if err != nil { + return err + } + methods := make([]MethodSig, nmethods) + for i := range methods { + methods[i], err = readMethodSig(data, pos) + if err != nil { + return err + } + } + m[iface] = methods + } + return nil +} + +func readMethodSig(data []byte, pos *int) (MethodSig, error) { + name, err := readName(data, pos) + if err != nil { + return MethodSig{}, err + } + mtype, err := readSymbol(data, pos) + if err != nil { + return MethodSig{}, err + } + return MethodSig{Name: name, MType: mtype}, nil +} + +func decodeUseIfaceMethod(data []byte, pos *int, m map[Symbol][]IfaceMethodDemand) error { + count, err := readCount(data, pos, "use interface method") + if err != nil { + return err + } + for range count { + owner, err := readSymbol(data, pos) + if err != nil { + return err + } + ndemands, err := readCount(data, pos, "interface method demands") + if err != nil { + return err + } + demands := make([]IfaceMethodDemand, ndemands) + for i := range demands { + target, err := readSymbol(data, pos) + if err != nil { + return err + } + sig, err := readMethodSig(data, pos) + if err != nil { + return err + } + demands[i] = IfaceMethodDemand{Target: target, Sig: sig} + } + m[owner] = demands + } + return nil +} + +func decodeMethodInfo(data []byte, pos *int, m map[Symbol][]MethodSlot) error { + count, err := readCount(data, pos, "method info") + if err != nil { + return err + } + for range count { + typ, err := readSymbol(data, pos) + if err != nil { + return err + } + nslots, err := readCount(data, pos, "method slots") + if err != nil { + return err + } + slots := make([]MethodSlot, nslots) + for i := range slots { + sig, err := readMethodSig(data, pos) + if err != nil { + return err + } + ifn, err := readSymbol(data, pos) + if err != nil { + return err + } + tfn, err := readSymbol(data, pos) + if err != nil { + return err + } + slots[i] = MethodSlot{Sig: sig, IFn: ifn, TFn: tfn} + } + m[typ] = slots + } + return nil +} + +func decodeUseNamedMethod(data []byte, pos *int, m map[Symbol][]Name) error { + count, err := readCount(data, pos, "use named method") + if err != nil { + return err + } + for range count { + owner, err := readSymbol(data, pos) + if err != nil { + return err + } + nnames, err := readCount(data, pos, "method names") + if err != nil { + return err + } + names := make([]Name, nnames) + for i := range names { + names[i], err = readName(data, pos) + if err != nil { + return err + } + } + m[owner] = names + } + return nil +} + +func decodeReflectMethod(data []byte, pos *int, m map[Symbol]struct{}) error { + count, err := readCount(data, pos, "reflect method") + if err != nil { + return err + } + for range count { + owner, err := readSymbol(data, pos) + if err != nil { + return err + } + m[owner] = struct{}{} + } + return nil +} diff --git a/internal/metadata/encode.go b/internal/metadata/encode.go new file mode 100644 index 0000000000..1d2feda808 --- /dev/null +++ b/internal/metadata/encode.go @@ -0,0 +1,137 @@ +package metadata + +import ( + "encoding/binary" + "io" + "sort" +) + +const ( + magicV1 = "LLPS" + version1 = 1 +) + +// WriteMeta serializes PackageMeta to the LLPS v1 binary format. +func (pm *PackageMeta) WriteMeta(w io.Writer) error { + buf := make([]byte, 0, 4096) + + buf = append(buf, magicV1...) + buf = binary.AppendUvarint(buf, version1) + + buf = binary.AppendUvarint(buf, uint64(len(pm.stringTable))) + for _, s := range pm.stringTable { + buf = binary.AppendUvarint(buf, uint64(len(s))) + buf = append(buf, s...) + } + + writeSymbolMap(&buf, pm.ordinaryEdges) + writeSymbolMap(&buf, pm.typeChildren) + writeInterfaceInfo(&buf, pm.interfaceInfo) + writeSymbolMap(&buf, pm.useIface) + writeUseIfaceMethod(&buf, pm.useIfaceMethod) + writeMethodInfo(&buf, pm.methodInfo) + writeUseNamedMethod(&buf, pm.useNamedMethod) + writeReflectMethod(&buf, pm.reflectMethod) + + _, err := w.Write(buf) + return err +} + +func writeSymbolMap(buf *[]byte, m map[Symbol][]Symbol) { + keys := sortedSymbolKeys(m) + *buf = binary.AppendUvarint(*buf, uint64(len(keys))) + for _, key := range keys { + values := m[key] + *buf = binary.AppendUvarint(*buf, uint64(key)) + *buf = binary.AppendUvarint(*buf, uint64(len(values))) + for _, value := range values { + *buf = binary.AppendUvarint(*buf, uint64(value)) + } + } +} + +func writeInterfaceInfo(buf *[]byte, m map[Symbol][]MethodSig) { + keys := sortedSymbolKeys(m) + *buf = binary.AppendUvarint(*buf, uint64(len(keys))) + for _, iface := range keys { + methods := m[iface] + *buf = binary.AppendUvarint(*buf, uint64(iface)) + *buf = binary.AppendUvarint(*buf, uint64(len(methods))) + for _, method := range methods { + writeMethodSig(buf, method) + } + } +} + +func writeMethodSig(buf *[]byte, sig MethodSig) { + *buf = binary.AppendUvarint(*buf, uint64(sig.Name)) + *buf = binary.AppendUvarint(*buf, uint64(sig.MType)) +} + +func writeUseIfaceMethod(buf *[]byte, m map[Symbol][]IfaceMethodDemand) { + keys := sortedSymbolKeys(m) + *buf = binary.AppendUvarint(*buf, uint64(len(keys))) + for _, owner := range keys { + demands := m[owner] + *buf = binary.AppendUvarint(*buf, uint64(owner)) + *buf = binary.AppendUvarint(*buf, uint64(len(demands))) + for _, demand := range demands { + *buf = binary.AppendUvarint(*buf, uint64(demand.Target)) + writeMethodSig(buf, demand.Sig) + } + } +} + +func writeMethodInfo(buf *[]byte, m map[Symbol][]MethodSlot) { + keys := sortedSymbolKeys(m) + *buf = binary.AppendUvarint(*buf, uint64(len(keys))) + for _, typ := range keys { + slots := m[typ] + *buf = binary.AppendUvarint(*buf, uint64(typ)) + *buf = binary.AppendUvarint(*buf, uint64(len(slots))) + for _, slot := range slots { + writeMethodSig(buf, slot.Sig) + *buf = binary.AppendUvarint(*buf, uint64(slot.IFn)) + *buf = binary.AppendUvarint(*buf, uint64(slot.TFn)) + } + } +} + +func writeUseNamedMethod(buf *[]byte, m map[Symbol][]Name) { + keys := sortedSymbolKeys(m) + *buf = binary.AppendUvarint(*buf, uint64(len(keys))) + for _, owner := range keys { + names := m[owner] + *buf = binary.AppendUvarint(*buf, uint64(owner)) + *buf = binary.AppendUvarint(*buf, uint64(len(names))) + for _, name := range names { + *buf = binary.AppendUvarint(*buf, uint64(name)) + } + } +} + +func writeReflectMethod(buf *[]byte, m map[Symbol]struct{}) { + keys := make([]Symbol, 0, len(m)) + for key := range m { + keys = append(keys, key) + } + sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) + + *buf = binary.AppendUvarint(*buf, uint64(len(keys))) + for _, owner := range keys { + *buf = binary.AppendUvarint(*buf, uint64(owner)) + } +} + +type symbolValueMap[V any] interface { + ~map[Symbol]V +} + +func sortedSymbolKeys[M symbolValueMap[V], V any](m M) []Symbol { + keys := make([]Symbol, 0, len(m)) + for key := range m { + keys = append(keys, key) + } + sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) + return keys +} diff --git a/internal/metadata/format.go b/internal/metadata/format.go new file mode 100644 index 0000000000..9939a76c4b --- /dev/null +++ b/internal/metadata/format.go @@ -0,0 +1,141 @@ +package metadata + +import ( + "fmt" + "io" + "sort" + "strings" +) + +// FormatMeta writes a stable human-readable representation for tests. +func FormatMeta(w io.Writer, pm *PackageMeta) { + if pm == nil { + return + } + + sym := func(s Symbol) string { + if int(s) < len(pm.stringTable) { + return pm.stringTable[s] + } + return fmt.Sprintf("?%d", s) + } + name := func(n Name) string { + if int(n) < len(pm.stringTable) { + return pm.stringTable[n] + } + return fmt.Sprintf("?%d", n) + } + keys := func(m map[Symbol][]Symbol) []Symbol { return sortedFormatKeys(m, sym) } + + if len(pm.typeChildren) > 0 { + fmt.Fprintln(w, "[TypeChildren]") + for _, key := range keys(pm.typeChildren) { + fmt.Fprintf(w, "%s:\n", sym(key)) + for _, child := range pm.typeChildren[key] { + fmt.Fprintf(w, " %s\n", sym(child)) + } + } + fmt.Fprintln(w) + } + + if len(pm.interfaceInfo) > 0 { + fmt.Fprintln(w, "[InterfaceInfo]") + for _, iface := range sortedFormatKeys(pm.interfaceInfo, sym) { + fmt.Fprintf(w, "%s:\n", sym(iface)) + for _, method := range pm.interfaceInfo[iface] { + fmt.Fprintf(w, " %s %s\n", name(method.Name), sym(method.MType)) + } + } + fmt.Fprintln(w) + } + + if len(pm.ordinaryEdges) > 0 { + fmt.Fprintln(w, "[OrdinaryEdges]") + for _, key := range keys(pm.ordinaryEdges) { + fmt.Fprintf(w, "%s:\n", sym(key)) + for _, dst := range pm.ordinaryEdges[key] { + fmt.Fprintf(w, " %s\n", sym(dst)) + } + } + fmt.Fprintln(w) + } + + if len(pm.useIface) > 0 { + fmt.Fprintln(w, "[UseIface]") + for _, key := range keys(pm.useIface) { + fmt.Fprintf(w, "%s:\n", sym(key)) + for _, typ := range pm.useIface[key] { + fmt.Fprintf(w, " %s\n", sym(typ)) + } + } + fmt.Fprintln(w) + } + + if len(pm.useIfaceMethod) > 0 { + fmt.Fprintln(w, "[UseIfaceMethod]") + for _, owner := range sortedFormatKeys(pm.useIfaceMethod, sym) { + fmt.Fprintf(w, "%s:\n", sym(owner)) + for _, demand := range pm.useIfaceMethod[owner] { + fmt.Fprintf(w, " %s %s %s\n", sym(demand.Target), name(demand.Sig.Name), sym(demand.Sig.MType)) + } + } + fmt.Fprintln(w) + } + + if len(pm.methodInfo) > 0 { + fmt.Fprintln(w, "[MethodInfo]") + for _, typ := range sortedFormatKeys(pm.methodInfo, sym) { + fmt.Fprintf(w, "%s:\n", sym(typ)) + for i, slot := range pm.methodInfo[typ] { + fmt.Fprintf(w, " %d %s %s %s %s\n", i, name(slot.Sig.Name), sym(slot.Sig.MType), sym(slot.IFn), sym(slot.TFn)) + } + } + fmt.Fprintln(w) + } + + if len(pm.useNamedMethod) > 0 { + fmt.Fprintln(w, "[UseNamedMethod]") + for _, owner := range sortedFormatKeys(pm.useNamedMethod, sym) { + fmt.Fprintf(w, "%s:\n", sym(owner)) + for _, methodName := range pm.useNamedMethod[owner] { + fmt.Fprintf(w, " %s\n", name(methodName)) + } + } + fmt.Fprintln(w) + } + + if len(pm.reflectMethod) > 0 { + owners := sortedFormatSetKeys(pm.reflectMethod, sym) + + fmt.Fprintln(w, "[ReflectMethod]") + for _, owner := range owners { + fmt.Fprintln(w, sym(owner)) + } + fmt.Fprintln(w) + } +} + +// MetaString returns the formatted metadata string. +func MetaString(pm *PackageMeta) string { + var sb strings.Builder + FormatMeta(&sb, pm) + return sb.String() +} + +func sortedFormatKeys[V any](m map[Symbol]V, name func(Symbol) string) []Symbol { + keys := make([]Symbol, 0, len(m)) + for key := range m { + keys = append(keys, key) + } + sort.Slice(keys, func(i, j int) bool { return name(keys[i]) < name(keys[j]) }) + return keys +} + +func sortedFormatSetKeys(m map[Symbol]struct{}, name func(Symbol) string) []Symbol { + keys := make([]Symbol, 0, len(m)) + for key := range m { + keys = append(keys, key) + } + sort.Slice(keys, func(i, j int) bool { return name(keys[i]) < name(keys[j]) }) + return keys +} diff --git a/internal/metadata/global_summary.go b/internal/metadata/global_summary.go new file mode 100644 index 0000000000..f0125e01c2 --- /dev/null +++ b/internal/metadata/global_summary.go @@ -0,0 +1,357 @@ +package metadata + +import ( + "fmt" + "reflect" + "sort" +) + +// GlobalSummary is a whole-program metadata view in one global Symbol/Name space. +type GlobalSummary struct { + stringTable []string + + symbolByText map[string]Symbol + nameByText map[string]Name + + ordinaryEdges map[Symbol][]Symbol + typeChildren map[Symbol][]Symbol + interfaceInfo map[Symbol][]MethodSig + useIface map[Symbol][]Symbol + useIfaceMethod map[Symbol][]IfaceMethodDemand + methodInfo map[Symbol][]MethodSlot + useNamedMethod map[Symbol][]Name + reflectMethod map[Symbol]struct{} +} + +// NewGlobalSummary merges package-local metadata into a whole-program view. +func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { + b := newGlobalSummaryBuilder() + for _, pm := range pkgs { + if pm == nil { + continue + } + r := newPackageRemapper(pm, b) + + pm.ForEachOrdinaryEdge(func(src Symbol, dsts []Symbol) { + gsrc := r.symbol(src) + for _, dst := range dsts { + addUniqueSymbol(&b.summary.ordinaryEdges, gsrc, r.symbol(dst)) + } + }) + pm.ForEachTypeChild(func(parent Symbol, children []Symbol) { + gparent := r.symbol(parent) + for _, child := range children { + addUniqueSymbol(&b.summary.typeChildren, gparent, r.symbol(child)) + } + }) + pm.ForEachInterface(func(iface Symbol, methods []MethodSig) { + giface := r.symbol(iface) + for _, method := range methods { + addUniqueMethodSig(&b.summary.interfaceInfo, giface, r.methodSig(method)) + } + }) + pm.ForEachUseIface(func(owner Symbol, types []Symbol) { + gowner := r.symbol(owner) + for _, typ := range types { + addUniqueSymbol(&b.summary.useIface, gowner, r.symbol(typ)) + } + }) + pm.ForEachUseIfaceMethod(func(owner Symbol, demands []IfaceMethodDemand) { + gowner := r.symbol(owner) + for _, demand := range demands { + addUniqueIfaceMethodDemand(&b.summary.useIfaceMethod, gowner, IfaceMethodDemand{ + Target: r.symbol(demand.Target), + Sig: r.methodSig(demand.Sig), + }) + } + }) + var methodErr error + pm.ForEachMethodInfo(func(typ Symbol, slots []MethodSlot) { + if methodErr != nil { + return + } + gtyp := r.symbol(typ) + gslots := make([]MethodSlot, 0, len(slots)) + for _, slot := range slots { + gslots = append(gslots, MethodSlot{ + Sig: r.methodSig(slot.Sig), + IFn: r.symbol(slot.IFn), + TFn: r.symbol(slot.TFn), + }) + } + methodErr = b.addMethodSlots(gtyp, gslots) + }) + if methodErr != nil { + return nil, methodErr + } + pm.ForEachUseNamedMethod(func(owner Symbol, names []Name) { + gowner := r.symbol(owner) + for _, name := range names { + addUniqueName(&b.summary.useNamedMethod, gowner, r.name(name)) + } + }) + pm.ForEachReflectMethod(func(owner Symbol) { + b.summary.reflectMethod[r.symbol(owner)] = struct{}{} + }) + } + return b.build(), nil +} + +// LookupSymbol returns a global Symbol for a module-level symbol name. +func (g *GlobalSummary) LookupSymbol(name string) (Symbol, bool) { + if g == nil { + return 0, false + } + sym, ok := g.symbolByText[name] + return sym, ok +} + +// SymbolName returns the text referenced by a global Symbol. +func (g *GlobalSummary) SymbolName(sym Symbol) string { + if g == nil || int(sym) >= len(g.stringTable) { + return "" + } + return g.stringTable[sym] +} + +// Name returns the text referenced by a global Name. +func (g *GlobalSummary) Name(ref Name) string { + if g == nil || int(ref) >= len(g.stringTable) { + return "" + } + return g.stringTable[ref] +} + +// Interfaces returns all interface type symbols known to the summary. +func (g *GlobalSummary) Interfaces() []Symbol { + if g == nil { + return nil + } + return sortedGlobalKeys(g.interfaceInfo, g.SymbolName) +} + +// ConcreteTypes returns all concrete type symbols with method slots. +func (g *GlobalSummary) ConcreteTypes() []Symbol { + if g == nil { + return nil + } + return sortedGlobalKeys(g.methodInfo, g.SymbolName) +} + +// OrdinaryEdges returns direct ordinary references from sym. +func (g *GlobalSummary) OrdinaryEdges(sym Symbol) []Symbol { + if g == nil { + return nil + } + return cloneSymbols(g.ordinaryEdges[sym]) +} + +// TypeChildren returns child type symbols for typ. +func (g *GlobalSummary) TypeChildren(typ Symbol) []Symbol { + if g == nil { + return nil + } + return cloneSymbols(g.typeChildren[typ]) +} + +// InterfaceMethods returns the method set for iface. +func (g *GlobalSummary) InterfaceMethods(iface Symbol) []MethodSig { + if g == nil { + return nil + } + return cloneMethodSigs(g.interfaceInfo[iface]) +} + +// UseIface returns concrete types that enter interface semantics from fn. +func (g *GlobalSummary) UseIface(fn Symbol) []Symbol { + if g == nil { + return nil + } + return cloneSymbols(g.useIface[fn]) +} + +// UseIfaceMethod returns interface method demands emitted by fn. +func (g *GlobalSummary) UseIfaceMethod(fn Symbol) []IfaceMethodDemand { + if g == nil { + return nil + } + return cloneIfaceMethodDemands(g.useIfaceMethod[fn]) +} + +// MethodSlots returns ABI method slots for typ. +func (g *GlobalSummary) MethodSlots(typ Symbol) []MethodSlot { + if g == nil { + return nil + } + return cloneMethodSlots(g.methodInfo[typ]) +} + +// UseNamedMethod returns constant MethodByName names emitted by fn. +func (g *GlobalSummary) UseNamedMethod(fn Symbol) []Name { + if g == nil { + return nil + } + return cloneNames(g.useNamedMethod[fn]) +} + +// HasReflectMethod reports whether fn triggers conservative reflection handling. +func (g *GlobalSummary) HasReflectMethod(fn Symbol) bool { + if g == nil { + return false + } + _, ok := g.reflectMethod[fn] + return ok +} + +type globalSummaryBuilder struct { + summary *GlobalSummary + idByText map[string]uint32 +} + +func newGlobalSummaryBuilder() *globalSummaryBuilder { + return &globalSummaryBuilder{ + summary: &GlobalSummary{ + symbolByText: make(map[string]Symbol), + nameByText: make(map[string]Name), + ordinaryEdges: make(map[Symbol][]Symbol), + typeChildren: make(map[Symbol][]Symbol), + interfaceInfo: make(map[Symbol][]MethodSig), + useIface: make(map[Symbol][]Symbol), + useIfaceMethod: make(map[Symbol][]IfaceMethodDemand), + methodInfo: make(map[Symbol][]MethodSlot), + useNamedMethod: make(map[Symbol][]Name), + reflectMethod: make(map[Symbol]struct{}), + }, + idByText: make(map[string]uint32), + } +} + +func (b *globalSummaryBuilder) internSymbol(text string) Symbol { + id := b.internText(text) + sym := Symbol(id) + b.summary.symbolByText[text] = sym + return sym +} + +func (b *globalSummaryBuilder) internName(text string) Name { + id := b.internText(text) + name := Name(id) + b.summary.nameByText[text] = name + return name +} + +func (b *globalSummaryBuilder) internText(text string) uint32 { + if id, ok := b.idByText[text]; ok { + return id + } + id := uint32(len(b.summary.stringTable)) + b.idByText[text] = id + b.summary.stringTable = append(b.summary.stringTable, text) + return id +} + +func (b *globalSummaryBuilder) addMethodSlots(typ Symbol, slots []MethodSlot) error { + if existing, ok := b.summary.methodInfo[typ]; ok { + if reflect.DeepEqual(existing, slots) { + return nil + } + return fmt.Errorf("conflicting MethodInfo for %s", b.summary.SymbolName(typ)) + } + b.summary.methodInfo[typ] = cloneMethodSlots(slots) + return nil +} + +func (b *globalSummaryBuilder) build() *GlobalSummary { + return b.summary +} + +type packageRemapper struct { + pm *PackageMeta + b *globalSummaryBuilder + + symbols map[Symbol]Symbol + names map[Name]Name +} + +func newPackageRemapper(pm *PackageMeta, b *globalSummaryBuilder) *packageRemapper { + return &packageRemapper{ + pm: pm, + b: b, + symbols: make(map[Symbol]Symbol), + names: make(map[Name]Name), + } +} + +func (r *packageRemapper) symbol(local Symbol) Symbol { + if global, ok := r.symbols[local]; ok { + return global + } + global := r.b.internSymbol(r.pm.SymbolName(local)) + r.symbols[local] = global + return global +} + +func (r *packageRemapper) name(local Name) Name { + if global, ok := r.names[local]; ok { + return global + } + global := r.b.internName(r.pm.Name(local)) + r.names[local] = global + return global +} + +func (r *packageRemapper) methodSig(local MethodSig) MethodSig { + return MethodSig{ + Name: r.name(local.Name), + MType: r.symbol(local.MType), + } +} + +func addUniqueSymbol(m *map[Symbol][]Symbol, key, value Symbol) { + values := (*m)[key] + for _, existing := range values { + if existing == value { + return + } + } + (*m)[key] = append(values, value) +} + +func addUniqueName(m *map[Symbol][]Name, key Symbol, value Name) { + values := (*m)[key] + for _, existing := range values { + if existing == value { + return + } + } + (*m)[key] = append(values, value) +} + +func addUniqueMethodSig(m *map[Symbol][]MethodSig, key Symbol, value MethodSig) { + values := (*m)[key] + for _, existing := range values { + if existing == value { + return + } + } + (*m)[key] = append(values, value) +} + +func addUniqueIfaceMethodDemand(m *map[Symbol][]IfaceMethodDemand, key Symbol, value IfaceMethodDemand) { + values := (*m)[key] + for _, existing := range values { + if existing == value { + return + } + } + (*m)[key] = append(values, value) +} + +func sortedGlobalKeys[V any](m map[Symbol]V, name func(Symbol) string) []Symbol { + keys := make([]Symbol, 0, len(m)) + for key := range m { + keys = append(keys, key) + } + sort.Slice(keys, func(i, j int) bool { return name(keys[i]) < name(keys[j]) }) + return keys +} diff --git a/internal/metadata/global_summary_test.go b/internal/metadata/global_summary_test.go new file mode 100644 index 0000000000..ce3f5093e8 --- /dev/null +++ b/internal/metadata/global_summary_test.go @@ -0,0 +1,158 @@ +package metadata + +import ( + "reflect" + "testing" +) + +func TestGlobalSummaryMergesLocalIDsByText(t *testing.T) { + pkgA, refsA := buildGlobalSummaryPkgA() + pkgB, refsB := buildGlobalSummaryPkgB() + + if refsA.intType == refsB.intType { + t.Fatal("test setup should use different local Symbol IDs for _llgo_int") + } + + summary, err := NewGlobalSummary([]*PackageMeta{pkgA, pkgB}) + if err != nil { + t.Fatalf("NewGlobalSummary: %v", err) + } + + intSymA, ok := summary.LookupSymbol("_llgo_int") + if !ok { + t.Fatal("LookupSymbol(_llgo_int) failed") + } + if got := summary.SymbolName(intSymA); got != "_llgo_int" { + t.Fatalf("SymbolName(_llgo_int) = %q", got) + } + + funcType, ok := summary.LookupSymbol("_llgo_func$X") + if !ok { + t.Fatal("LookupSymbol(_llgo_func$X) failed") + } + mainA := mustLookupSymbol(t, summary, "pkg/a.main") + useA := mustLookupSymbol(t, summary, "pkg/a.use") + typeB := mustLookupSymbol(t, summary, "_llgo_pkg/b.T") + ifaceB := mustLookupSymbol(t, summary, "_llgo_iface$B") + ifnB := mustLookupSymbol(t, summary, "pkg/b.(*T).M") + tfnB := mustLookupSymbol(t, summary, "pkg/b.T.M") + + if got := summary.OrdinaryEdges(mainA); !reflect.DeepEqual(got, []Symbol{useA}) { + t.Fatalf("OrdinaryEdges(pkg/a.main) = %#v, want %#v", got, []Symbol{useA}) + } + if got := summary.TypeChildren(typeB); !reflect.DeepEqual(got, []Symbol{intSymA}) { + t.Fatalf("TypeChildren(_llgo_pkg/b.T) = %#v, want %#v", got, []Symbol{intSymA}) + } + if got := summary.UseIface(mainA); !reflect.DeepEqual(got, []Symbol{typeB}) { + t.Fatalf("UseIface(pkg/a.main) = %#v, want %#v", got, []Symbol{typeB}) + } + + methodName := summary.UseNamedMethod(useA) + if len(methodName) != 1 || summary.Name(methodName[0]) != "M" { + t.Fatalf("UseNamedMethod(pkg/a.use) = %#v, want name M", methodName) + } + if _, ok := summary.LookupSymbol("M"); ok { + t.Fatal("LookupSymbol found method name that only appeared as Name") + } + + wantSig := MethodSig{Name: methodName[0], MType: funcType} + if got := summary.InterfaceMethods(ifaceB); !reflect.DeepEqual(got, []MethodSig{wantSig}) { + t.Fatalf("InterfaceMethods(_llgo_iface$B) = %#v, want %#v", got, []MethodSig{wantSig}) + } + if got := summary.UseIfaceMethod(useA); !reflect.DeepEqual(got, []IfaceMethodDemand{{Target: ifaceB, Sig: wantSig}}) { + t.Fatalf("UseIfaceMethod(pkg/a.use) = %#v", got) + } + if got := summary.MethodSlots(typeB); !reflect.DeepEqual(got, []MethodSlot{{Sig: wantSig, IFn: ifnB, TFn: tfnB}}) { + t.Fatalf("MethodSlots(_llgo_pkg/b.T) = %#v", got) + } + if !summary.HasReflectMethod(useA) { + t.Fatal("HasReflectMethod(pkg/a.use) = false, want true") + } + + if got := summary.Interfaces(); !reflect.DeepEqual(got, []Symbol{ifaceB}) { + t.Fatalf("Interfaces() = %#v, want %#v", got, []Symbol{ifaceB}) + } + if got := summary.ConcreteTypes(); !reflect.DeepEqual(got, []Symbol{typeB}) { + t.Fatalf("ConcreteTypes() = %#v, want %#v", got, []Symbol{typeB}) + } +} + +func TestGlobalSummaryRejectsConflictingMethodSlots(t *testing.T) { + pkgA, _ := buildGlobalSummaryPkgB() + + // Rebuild pkgB with the same type and slot signature but a different TFn. + b := NewBuilder() + typ := b.Symbol("_llgo_pkg/b.T") + methodName := b.Name("M") + funcType := b.Symbol("_llgo_func$X") + ifn := b.Symbol("pkg/b.(*T).M") + tfn := b.Symbol("pkg/b.T.M.conflict") + b.AddMethodInfo(typ, []MethodSlot{{ + Sig: MethodSig{Name: methodName, MType: funcType}, + IFn: ifn, + TFn: tfn, + }}) + pkgB := b.Build() + + if _, err := NewGlobalSummary([]*PackageMeta{pkgA, pkgB}); err == nil { + t.Fatal("NewGlobalSummary accepted conflicting MethodInfo for the same type") + } +} + +type globalSummaryRefs struct { + intType Symbol +} + +func buildGlobalSummaryPkgA() (*PackageMeta, globalSummaryRefs) { + b := NewBuilder() + main := b.Symbol("pkg/a.main") + use := b.Symbol("pkg/a.use") + methodName := b.Name("M") + typ := b.Symbol("_llgo_pkg/b.T") + iface := b.Symbol("_llgo_iface$B") + funcType := b.Symbol("_llgo_func$X") + intType := b.Symbol("_llgo_int") + + b.AddEdge(main, use) + b.AddUseIface(main, []Symbol{typ}) + b.AddUseIfaceMethod(use, []IfaceMethodDemand{{ + Target: iface, + Sig: MethodSig{Name: methodName, MType: funcType}, + }}) + b.AddUseNamedMethod(use, []Name{methodName}) + b.AddReflectMethod(use) + + return b.Build(), globalSummaryRefs{intType: intType} +} + +func buildGlobalSummaryPkgB() (*PackageMeta, globalSummaryRefs) { + b := NewBuilder() + other := b.Symbol("pkg/b.other") + intType := b.Symbol("_llgo_int") + typ := b.Symbol("_llgo_pkg/b.T") + methodName := b.Name("M") + funcType := b.Symbol("_llgo_func$X") + iface := b.Symbol("_llgo_iface$B") + ifn := b.Symbol("pkg/b.(*T).M") + tfn := b.Symbol("pkg/b.T.M") + + b.AddEdge(other, intType) + b.AddTypeChild(typ, intType) + b.AddIfaceEntry(iface, []MethodSig{{Name: methodName, MType: funcType}}) + b.AddMethodInfo(typ, []MethodSlot{{ + Sig: MethodSig{Name: methodName, MType: funcType}, + IFn: ifn, + TFn: tfn, + }}) + + return b.Build(), globalSummaryRefs{intType: intType} +} + +func mustLookupSymbol(t *testing.T, summary *GlobalSummary, name string) Symbol { + t.Helper() + sym, ok := summary.LookupSymbol(name) + if !ok { + t.Fatalf("LookupSymbol(%q) failed", name) + } + return sym +} diff --git a/internal/metadata/meta.go b/internal/metadata/meta.go new file mode 100644 index 0000000000..2a0bc25f62 --- /dev/null +++ b/internal/metadata/meta.go @@ -0,0 +1,181 @@ +// Package metadata defines package summary facts and their LLPS binary format. +package metadata + +// Symbol is a module-level named entity participating in reachability. +type Symbol uint32 + +// Name is a plain name reference used for semantic matching. +type Name uint32 + +// MethodSig describes a method by short name and function type symbol. +type MethodSig struct { + Name Name + MType Symbol +} + +// MethodSlot describes one entry in a concrete type's ABI method table. +type MethodSlot struct { + Sig MethodSig + IFn Symbol + TFn Symbol +} + +// IfaceMethodDemand records one reachable interface method call. +type IfaceMethodDemand struct { + Target Symbol + Sig MethodSig +} + +// PackageMeta holds all single-package facts needed by whole-program analysis. +type PackageMeta struct { + stringTable []string + + ordinaryEdges map[Symbol][]Symbol + typeChildren map[Symbol][]Symbol + interfaceInfo map[Symbol][]MethodSig + useIface map[Symbol][]Symbol + useIfaceMethod map[Symbol][]IfaceMethodDemand + methodInfo map[Symbol][]MethodSlot + useNamedMethod map[Symbol][]Name + reflectMethod map[Symbol]struct{} +} + +// NewPackageMeta creates an empty PackageMeta with initialized maps. +func NewPackageMeta(stringTable []string) *PackageMeta { + table := append([]string(nil), stringTable...) + return &PackageMeta{ + stringTable: table, + ordinaryEdges: make(map[Symbol][]Symbol), + typeChildren: make(map[Symbol][]Symbol), + interfaceInfo: make(map[Symbol][]MethodSig), + useIface: make(map[Symbol][]Symbol), + useIfaceMethod: make(map[Symbol][]IfaceMethodDemand), + methodInfo: make(map[Symbol][]MethodSlot), + useNamedMethod: make(map[Symbol][]Name), + reflectMethod: make(map[Symbol]struct{}), + } +} + +// StringTable returns a copy of the package-local string table. +func (pm *PackageMeta) StringTable() []string { + if pm == nil { + return nil + } + return append([]string(nil), pm.stringTable...) +} + +// SymbolName returns the string referenced by a Symbol. +func (pm *PackageMeta) SymbolName(sym Symbol) string { + if pm == nil || int(sym) >= len(pm.stringTable) { + return "" + } + return pm.stringTable[sym] +} + +// Name returns the string referenced by a Name. +func (pm *PackageMeta) Name(ref Name) string { + if pm == nil || int(ref) >= len(pm.stringTable) { + return "" + } + return pm.stringTable[ref] +} + +// ForEachOrdinaryEdge visits each ordinary reachability edge group. +func (pm *PackageMeta) ForEachOrdinaryEdge(fn func(src Symbol, dsts []Symbol)) { + if pm == nil { + return + } + for src, dsts := range pm.ordinaryEdges { + fn(src, cloneSymbols(dsts)) + } +} + +// ForEachTypeChild visits each type-child edge group. +func (pm *PackageMeta) ForEachTypeChild(fn func(parent Symbol, children []Symbol)) { + if pm == nil { + return + } + for parent, children := range pm.typeChildren { + fn(parent, cloneSymbols(children)) + } +} + +// ForEachInterface visits each interface method set. +func (pm *PackageMeta) ForEachInterface(fn func(iface Symbol, methods []MethodSig)) { + if pm == nil { + return + } + for iface, methods := range pm.interfaceInfo { + fn(iface, cloneMethodSigs(methods)) + } +} + +// ForEachUseIface visits each function's concrete types used as interfaces. +func (pm *PackageMeta) ForEachUseIface(fn func(owner Symbol, types []Symbol)) { + if pm == nil { + return + } + for owner, types := range pm.useIface { + fn(owner, cloneSymbols(types)) + } +} + +// ForEachUseIfaceMethod visits each function's interface method demands. +func (pm *PackageMeta) ForEachUseIfaceMethod(fn func(owner Symbol, demands []IfaceMethodDemand)) { + if pm == nil { + return + } + for owner, demands := range pm.useIfaceMethod { + fn(owner, cloneIfaceMethodDemands(demands)) + } +} + +// ForEachMethodInfo visits each concrete type's method slots. +func (pm *PackageMeta) ForEachMethodInfo(fn func(typ Symbol, slots []MethodSlot)) { + if pm == nil { + return + } + for typ, slots := range pm.methodInfo { + fn(typ, cloneMethodSlots(slots)) + } +} + +// ForEachUseNamedMethod visits each function's constant MethodByName names. +func (pm *PackageMeta) ForEachUseNamedMethod(fn func(owner Symbol, names []Name)) { + if pm == nil { + return + } + for owner, names := range pm.useNamedMethod { + fn(owner, cloneNames(names)) + } +} + +// ForEachReflectMethod visits each function that needs conservative reflection handling. +func (pm *PackageMeta) ForEachReflectMethod(fn func(owner Symbol)) { + if pm == nil { + return + } + for owner := range pm.reflectMethod { + fn(owner) + } +} + +func cloneSymbols(in []Symbol) []Symbol { + return append([]Symbol(nil), in...) +} + +func cloneNames(in []Name) []Name { + return append([]Name(nil), in...) +} + +func cloneMethodSigs(in []MethodSig) []MethodSig { + return append([]MethodSig(nil), in...) +} + +func cloneIfaceMethodDemands(in []IfaceMethodDemand) []IfaceMethodDemand { + return append([]IfaceMethodDemand(nil), in...) +} + +func cloneMethodSlots(in []MethodSlot) []MethodSlot { + return append([]MethodSlot(nil), in...) +} diff --git a/internal/metadata/metadata_test.go b/internal/metadata/metadata_test.go new file mode 100644 index 0000000000..e23437c7f8 --- /dev/null +++ b/internal/metadata/metadata_test.go @@ -0,0 +1,220 @@ +package metadata + +import ( + "bytes" + "reflect" + "strings" + "testing" +) + +func TestBuilderSeparatesSymbolAndNameReferences(t *testing.T) { + b := NewBuilder() + + mainSym := b.Symbol("main") + mainName := b.Name("main") + readName := b.Name("Read") + fnType := b.Symbol("_llgo_func$A1") + typ := b.Symbol("*reader") + ifn := b.Symbol("(*reader).Read$iface") + tfn := b.Symbol("(*reader).Read") + + if uint32(mainSym) != uint32(mainName) { + t.Fatalf("shared string table should allow equal backing IDs for equal text: Symbol=%d Name=%d", mainSym, mainName) + } + + b.AddEdge(mainSym, tfn) + b.AddUseNamedMethod(mainSym, []Name{mainName, readName}) + b.AddMethodInfo(typ, []MethodSlot{{ + Sig: MethodSig{Name: readName, MType: fnType}, + IFn: ifn, + TFn: tfn, + }}) + + pm := b.Build() + if got := pm.SymbolName(mainSym); got != "main" { + t.Fatalf("SymbolName(main) = %q, want main", got) + } + if got := pm.Name(mainName); got != "main" { + t.Fatalf("Name(main) = %q, want main", got) + } + slots := collectMethodSlots(pm, typ) + if got := pm.Name(slots[0].Sig.Name); got != "Read" { + t.Fatalf("method name = %q, want Read", got) + } + if got := pm.SymbolName(slots[0].Sig.MType); got != "_llgo_func$A1" { + t.Fatalf("method type = %q, want _llgo_func$A1", got) + } +} + +func TestWriteReadMetaRoundTrip(t *testing.T) { + pm := buildFullTestMeta() + + var buf bytes.Buffer + if err := pm.WriteMeta(&buf); err != nil { + t.Fatalf("WriteMeta: %v", err) + } + + got, err := ReadMeta(bytes.NewReader(buf.Bytes())) + if err != nil { + t.Fatalf("ReadMeta: %v", err) + } + + if !reflect.DeepEqual(got, pm) { + t.Fatalf("round trip mismatch\ngot: %#v\nwant: %#v", got, pm) + } + + var buf2 bytes.Buffer + if err := got.WriteMeta(&buf2); err != nil { + t.Fatalf("WriteMeta after round trip: %v", err) + } + if !bytes.Equal(buf.Bytes(), buf2.Bytes()) { + t.Fatalf("WriteMeta should be deterministic across round trip") + } +} + +func TestReadMetaRejectsInvalidFiles(t *testing.T) { + if _, err := ReadMeta(strings.NewReader("NOPE")); err == nil { + t.Fatal("ReadMeta accepted bad magic") + } + + var buf bytes.Buffer + pm := buildFullTestMeta() + if err := pm.WriteMeta(&buf); err != nil { + t.Fatal(err) + } + data := append([]byte(nil), buf.Bytes()...) + data[4] = 99 + if _, err := ReadMeta(bytes.NewReader(data)); err == nil { + t.Fatal("ReadMeta accepted unsupported version") + } +} + +func TestFormatMetaStableOutput(t *testing.T) { + got := MetaString(buildFullTestMeta()) + want := `[TypeChildren] +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner: + _llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner +*_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer: + _llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer +*_llgo_int: + _llgo_int +*_llgo_string: + _llgo_string +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer: + _llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner + _llgo_int + +[InterfaceInfo] +_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[OrdinaryEdges] +github.com/goplus/llgo/cl/_testmeta/interface_anonymous.main: + github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_anonymous.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use: + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).M + 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).N github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).N +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T.M + 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).N github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T.N + +[UseNamedMethod] +github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use: + M + +[ReflectMethod] +github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use + +` + if got != want { + t.Fatalf("MetaString mismatch\ngot:\n%s\nwant:\n%s", got, want) + } +} + +func buildFullTestMeta() *PackageMeta { + b := NewBuilder() + + nestedFuncPtr := b.Symbol("*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to") + nestedFunc := b.Symbol("_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to") + nestedInnerPtr := b.Symbol("*_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner") + nestedInner := b.Symbol("_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner") + nestedOuterPtr := b.Symbol("*_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer") + nestedOuter := b.Symbol("_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer") + intPtr := b.Symbol("*_llgo_int") + intType := b.Symbol("_llgo_int") + stringPtr := b.Symbol("*_llgo_string") + stringType := b.Symbol("_llgo_string") + + b.AddTypeChild(nestedFuncPtr, nestedFunc) + b.AddTypeChild(nestedInnerPtr, nestedInner) + b.AddTypeChild(nestedOuterPtr, nestedOuter) + b.AddTypeChild(intPtr, intType) + b.AddTypeChild(stringPtr, stringType) + b.AddTypeChild(nestedFunc, stringType) + b.AddTypeChild(nestedInner, stringType) + b.AddTypeChild(nestedOuter, nestedInner) + b.AddTypeChild(nestedOuter, intType) + + main := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.main") + use := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use") + iface := b.Symbol("_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo") + typ := b.Symbol("_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T") + ptrTyp := b.Symbol("*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T") + methodType := b.Symbol("_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac") + mName := b.Name("M") + nName := b.Name("N") + mSig := MethodSig{Name: mName, MType: methodType} + nSig := MethodSig{Name: nName, MType: methodType} + ptrM := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).M") + ptrN := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).N") + valM := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T.M") + valN := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T.N") + + b.AddEdge(main, use) + b.AddIfaceEntry(iface, []MethodSig{mSig, nSig}) + b.AddUseIface(main, []Symbol{typ}) + b.AddUseIfaceMethod(use, []IfaceMethodDemand{ + {Target: iface, Sig: mSig}, + {Target: iface, Sig: nSig}, + }) + b.AddMethodInfo(ptrTyp, []MethodSlot{ + {Sig: mSig, IFn: ptrM, TFn: ptrM}, + {Sig: nSig, IFn: ptrN, TFn: ptrN}, + }) + b.AddMethodInfo(typ, []MethodSlot{ + {Sig: mSig, IFn: ptrM, TFn: valM}, + {Sig: nSig, IFn: ptrN, TFn: valN}, + }) + b.AddUseNamedMethod(use, []Name{mName}) + b.AddReflectMethod(use) + + return b.Build() +} + +func collectMethodSlots(pm *PackageMeta, want Symbol) []MethodSlot { + var got []MethodSlot + pm.ForEachMethodInfo(func(typ Symbol, slots []MethodSlot) { + if typ == want { + got = append(got, slots...) + } + }) + return got +} From 3b5392c2e728bb1cacff65b6d4f15da17e2bfba5 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 15:42:33 +0800 Subject: [PATCH 02/37] wip: persist package summary metadata in build cache --- internal/build/build.go | 5 ++ internal/build/cache.go | 51 ++++++++++++- internal/build/cache_test.go | 36 ++++++++- internal/build/collect.go | 13 ++++ internal/build/collect_test.go | 131 +++++++++++++++++++++++++++++++++ 5 files changed, 234 insertions(+), 2 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index b0fce9f3f8..09f6c310e8 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -46,6 +46,7 @@ import ( "github.com/goplus/llgo/internal/flash" "github.com/goplus/llgo/internal/goembed" "github.com/goplus/llgo/internal/header" + "github.com/goplus/llgo/internal/metadata" "github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/internal/monitor" "github.com/goplus/llgo/internal/optlevel" @@ -1271,6 +1272,9 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { check(err) aPkg.LPkg = ret + if !aPkg.CacheHit && aPkg.Meta == nil { + aPkg.Meta = metadata.NewBuilder().Build() + } if hook := ctx.buildConf.ModuleHook; hook != nil { hook(aPkg) } @@ -1554,6 +1558,7 @@ type aPackage struct { LinkArgs []string ObjFiles []string // object files: .o or .ll (output of compiler, input to archiver) ArchiveFile string // archive file: .a (output of archiver, used for linking) + Meta *metadata.PackageMeta rewriteVars map[string]string // Cache related fields diff --git a/internal/build/cache.go b/internal/build/cache.go index f8870f6d92..28a3ceb6c8 100644 --- a/internal/build/cache.go +++ b/internal/build/cache.go @@ -25,12 +25,14 @@ import ( "strings" "github.com/goplus/llgo/internal/env" + "github.com/goplus/llgo/internal/metadata" ) const ( cacheBuildDirName = "build" cacheArchiveExt = ".a" cacheManifestExt = ".manifest" + cacheMetaExt = ".meta" ) // cacheRootFunc can be overridden for testing @@ -56,6 +58,7 @@ type cachePaths struct { Dir string // Directory containing cache files Archive string // Path to .a file Manifest string // Path to .manifest file + Meta string // Path to .meta file } // PackagePaths returns the cache paths for a package @@ -66,6 +69,7 @@ func (cm *cacheManager) PackagePaths(targetTriple, pkgPath, fingerprint string) Dir: dir, Archive: filepath.Join(dir, fingerprint+cacheArchiveExt), Manifest: filepath.Join(dir, fingerprint+cacheManifestExt), + Meta: filepath.Join(dir, fingerprint+cacheMetaExt), } } @@ -174,15 +178,60 @@ func readManifest(path string) (string, error) { return string(content), nil } +// writeMeta writes package summary metadata to a file atomically. +func writeMeta(path string, meta *metadata.PackageMeta) error { + if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { + return fmt.Errorf("create meta dir: %w", err) + } + tmp, err := os.CreateTemp(filepath.Dir(path), filepath.Base(path)+".tmp-*") + if err != nil { + return fmt.Errorf("create temp meta: %w", err) + } + tmpName := tmp.Name() + cleanup := true + defer func() { + if cleanup { + tmp.Close() + os.Remove(tmpName) + } + }() + + if err := meta.WriteMeta(tmp); err != nil { + return fmt.Errorf("write meta: %w", err) + } + if err := tmp.Close(); err != nil { + return fmt.Errorf("close meta: %w", err) + } + if err := os.Rename(tmpName, path); err != nil { + return fmt.Errorf("publish meta: %w", err) + } + + cleanup = false + return nil +} + +// readMeta reads package summary metadata from a file. +func readMeta(path string) (*metadata.PackageMeta, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + return metadata.ReadMeta(file) +} + // cacheExists checks if a valid cache entry exists func (cm *cacheManager) cacheExists(paths cachePaths) bool { - // Both archive and manifest must exist + // Archive, manifest, and valid package metadata must exist. if _, err := os.Stat(paths.Archive); err != nil { return false } if _, err := os.Stat(paths.Manifest); err != nil { return false } + if _, err := readMeta(paths.Meta); err != nil { + return false + } return true } diff --git a/internal/build/cache_test.go b/internal/build/cache_test.go index 6c6d4a0eb2..1444575eed 100644 --- a/internal/build/cache_test.go +++ b/internal/build/cache_test.go @@ -19,10 +19,13 @@ package build import ( + "bytes" "os" "path/filepath" "strings" "testing" + + "github.com/goplus/llgo/internal/metadata" ) func TestSanitizePkgPath(t *testing.T) { @@ -69,6 +72,11 @@ func TestCacheManager_PackagePaths(t *testing.T) { if paths.Manifest != expectedManifest { t.Errorf("Manifest = %q, want %q", paths.Manifest, expectedManifest) } + + expectedMeta := filepath.Join(expectedDir, "abc123.meta") + if paths.Meta != expectedMeta { + t.Errorf("Meta = %q, want %q", paths.Meta, expectedMeta) + } } func TestCacheManager_EnsureDir(t *testing.T) { @@ -177,9 +185,35 @@ func TestCacheManager_CacheExists(t *testing.T) { // Create manifest os.WriteFile(paths.Manifest, []byte("manifest"), 0644) + // Still should not exist (meta missing) + if cm.cacheExists(paths) { + t.Error("cache should not exist without meta") + } + + // Create invalid meta + os.WriteFile(paths.Meta, []byte("bad meta"), 0644) + if cm.cacheExists(paths) { + t.Error("cache should not exist with invalid meta") + } + + // Create valid meta + writeTestMetaFile(t, paths.Meta) + // Now should exist if !cm.cacheExists(paths) { - t.Error("cache should exist with both files") + t.Error("cache should exist with archive, manifest, and valid meta") + } +} + +func writeTestMetaFile(t *testing.T, path string) { + t.Helper() + var buf bytes.Buffer + pm := metadata.NewBuilder().Build() + if err := pm.WriteMeta(&buf); err != nil { + t.Fatalf("WriteMeta: %v", err) + } + if err := os.WriteFile(path, buf.Bytes(), 0o644); err != nil { + t.Fatalf("WriteFile %s: %v", path, err) } } diff --git a/internal/build/collect.go b/internal/build/collect.go index bdd1977cd0..3a991d8b58 100644 --- a/internal/build/collect.go +++ b/internal/build/collect.go @@ -27,6 +27,7 @@ import ( "strings" "github.com/goplus/llgo/internal/env" + "github.com/goplus/llgo/internal/metadata" "github.com/goplus/llgo/internal/packages" gopackages "golang.org/x/tools/go/packages" ) @@ -339,6 +340,10 @@ func (c *context) tryLoadFromCache(pkg *aPackage) bool { if err != nil { return false } + pkgMeta, err := readMeta(paths.Meta) + if err != nil { + return false + } // Parse metadata from manifest [Package] section (INI format) meta, err := parseManifestMetadata(content) @@ -351,6 +356,7 @@ func (c *context) tryLoadFromCache(pkg *aPackage) bool { pkg.LinkArgs = meta.LinkArgs pkg.NeedRt = meta.NeedRt pkg.NeedPyInit = meta.NeedPyInit + pkg.Meta = pkgMeta pkg.CacheHit = true return true @@ -458,6 +464,13 @@ func (c *context) saveToCache(pkg *aPackage) error { return nil } + if pkg.Meta == nil { + pkg.Meta = metadata.NewBuilder().Build() + } + if err := writeMeta(paths.Meta, pkg.Meta); err != nil { + return err + } + // Append metadata to existing manifest (pkg.Manifest was built in collectFingerprint). manifestContent := pkg.Manifest if manifestContent == "" { diff --git a/internal/build/collect_test.go b/internal/build/collect_test.go index 8f8070122e..fdb41d9c49 100644 --- a/internal/build/collect_test.go +++ b/internal/build/collect_test.go @@ -27,6 +27,7 @@ import ( "testing" "github.com/goplus/llgo/internal/crosscompile" + "github.com/goplus/llgo/internal/metadata" "github.com/goplus/llgo/internal/packages" gopackages "golang.org/x/tools/go/packages" ) @@ -409,6 +410,7 @@ func TestTryLoadFromCache_ForceRebuild(t *testing.T) { m.pkg.PkgPath = "example.com/cached" return m.Build() }(), + Meta: metadata.NewBuilder().Build(), } // Create a temporary .o file @@ -533,6 +535,7 @@ func TestSaveToCache_Success(t *testing.T) { return m.Build() }(), ObjFiles: []string{objFile.Name()}, + Meta: metadata.NewBuilder().Build(), } if err := ctx.saveToCache(pkg); err != nil { @@ -563,6 +566,134 @@ func TestSaveToCache_Success(t *testing.T) { if _, err := os.Stat(paths.Archive); err != nil { t.Errorf("archive should exist: %v", err) } + + metaFile, err := os.Open(paths.Meta) + if err != nil { + t.Errorf("meta should exist: %v", err) + } else { + defer metaFile.Close() + if _, err := metadata.ReadMeta(metaFile); err != nil { + t.Errorf("meta should be readable: %v", err) + } + } +} + +func TestTryLoadFromCache_LoadsPackageMeta(t *testing.T) { + td := t.TempDir() + oldFunc := cacheRootFunc + cacheRootFunc = func() string { return td } + defer func() { cacheRootFunc = oldFunc }() + + ctx := &context{ + conf: &packages.Config{}, + buildConf: &Config{ + Goos: "darwin", + Goarch: "arm64", + }, + crossCompile: crosscompile.Export{ + LLVMTarget: "arm64-apple-darwin", + }, + } + + objFile, err := os.CreateTemp(td, "test-*.o") + if err != nil { + t.Fatalf("CreateTemp: %v", err) + } + objFile.WriteString("fake object file") + objFile.Close() + + builder := metadata.NewBuilder() + main := builder.Symbol("pkg.main") + helper := builder.Symbol("pkg.helper") + builder.AddEdge(main, helper) + + pkg := &aPackage{ + Package: &packages.Package{ + PkgPath: "example.com/loadmeta", + Name: "loadmeta", + }, + Fingerprint: "loadmeta123", + Manifest: func() string { + m := newManifestBuilder() + m.env.Goos = "darwin" + m.pkg.PkgPath = "example.com/loadmeta" + return m.Build() + }(), + ObjFiles: []string{objFile.Name()}, + Meta: builder.Build(), + } + + if err := ctx.saveToCache(pkg); err != nil { + t.Fatalf("saveToCache: %v", err) + } + + pkg.ObjFiles = nil + pkg.ArchiveFile = "" + pkg.CacheHit = false + pkg.Meta = nil + + if !ctx.tryLoadFromCache(pkg) { + t.Fatal("tryLoadFromCache = false, want true") + } + if pkg.Meta == nil { + t.Fatal("Meta was not loaded from cache") + } + var edges []metadata.Symbol + pkg.Meta.ForEachOrdinaryEdge(func(src metadata.Symbol, dsts []metadata.Symbol) { + if pkg.Meta.SymbolName(src) == "pkg.main" { + edges = append(edges, dsts...) + } + }) + if len(edges) != 1 || pkg.Meta.SymbolName(edges[0]) != "pkg.helper" { + t.Fatalf("cached metadata edge mismatch: %#v", edges) + } +} + +func TestTryLoadFromCacheRejectsBadMeta(t *testing.T) { + td := t.TempDir() + oldFunc := cacheRootFunc + cacheRootFunc = func() string { return td } + defer func() { cacheRootFunc = oldFunc }() + + ctx := &context{ + conf: &packages.Config{}, + buildConf: &Config{ + Goos: "darwin", + Goarch: "arm64", + }, + crossCompile: crosscompile.Export{ + LLVMTarget: "arm64-apple-darwin", + }, + } + + pkg := &aPackage{ + Package: &packages.Package{ + PkgPath: "example.com/badmeta", + Name: "badmeta", + }, + Fingerprint: "badmeta123", + } + cm := ctx.ensureCacheManager() + paths := cm.PackagePaths("arm64-apple-darwin", "example.com/badmeta", "badmeta123") + if err := cm.EnsureDir(paths); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(paths.Archive, []byte("archive"), 0o644); err != nil { + t.Fatal(err) + } + m := newManifestBuilder() + m.env.Goos = "darwin" + m.pkg.PkgPath = "example.com/badmeta" + if err := writeManifest(paths.Manifest, m.Build()); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(paths.Meta, []byte("bad meta"), 0o644); err != nil { + t.Fatal(err) + } + + if ctx.tryLoadFromCache(pkg) { + t.Fatal("tryLoadFromCache accepted invalid meta") + } } func TestGetLLVMVersion(t *testing.T) { From b1ea4dba1e4157e3230b42f83f2f7cb1d94c5086 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 16:44:07 +0800 Subject: [PATCH 03/37] wip: emit semantic metadata summaries --- chore/gentests/gentests.go | 29 ++++++++ cl/_testmeta/ifaceuse_basic/in.go | 11 +++ cl/_testmeta/ifaceuse_basic/meta-expect.txt | 4 ++ cl/_testmeta/interface_anyonmous/in.go | 18 +++++ .../interface_anyonmous/meta-expect.txt | 22 ++++++ cl/_testmeta/interface_named/in.go | 17 +++++ cl/_testmeta/interface_named/meta-expect.txt | 18 +++++ cl/_testmeta/interface_unexported/in.go | 17 +++++ .../interface_unexported/meta-expect.txt | 18 +++++ cl/_testmeta/methodinfo_imported/in.go | 11 +++ .../methodinfo_imported/meta-expect.txt | 42 +++++++++++ cl/_testmeta/reflect_dynamic/in.go | 15 ++++ cl/_testmeta/reflect_dynamic/meta-expect.txt | 13 ++++ cl/_testmeta/reflect_named/in.go | 13 ++++ cl/_testmeta/reflect_named/meta-expect.txt | 61 ++++++++++++++++ cl/cltest/cltest.go | 60 +++++++++++++++- cl/compile.go | 10 +-- cl/compile_test.go | 8 +++ cl/embed_with_map_compile_test.go | 2 +- cl/instr.go | 61 ++++++++++++++++ internal/build/build.go | 10 ++- internal/metadata/builder.go | 69 +++++++++++++++++-- ssa/abitype.go | 34 ++++++++- ssa/expr.go | 13 ++++ ssa/interface.go | 29 ++++++++ ssa/package.go | 2 + 26 files changed, 587 insertions(+), 20 deletions(-) create mode 100644 cl/_testmeta/ifaceuse_basic/in.go create mode 100644 cl/_testmeta/ifaceuse_basic/meta-expect.txt create mode 100644 cl/_testmeta/interface_anyonmous/in.go create mode 100644 cl/_testmeta/interface_anyonmous/meta-expect.txt create mode 100644 cl/_testmeta/interface_named/in.go create mode 100644 cl/_testmeta/interface_named/meta-expect.txt create mode 100644 cl/_testmeta/interface_unexported/in.go create mode 100644 cl/_testmeta/interface_unexported/meta-expect.txt create mode 100644 cl/_testmeta/methodinfo_imported/in.go create mode 100644 cl/_testmeta/methodinfo_imported/meta-expect.txt create mode 100644 cl/_testmeta/reflect_dynamic/in.go create mode 100644 cl/_testmeta/reflect_dynamic/meta-expect.txt create mode 100644 cl/_testmeta/reflect_named/in.go create mode 100644 cl/_testmeta/reflect_named/meta-expect.txt diff --git a/chore/gentests/gentests.go b/chore/gentests/gentests.go index 979f3456e7..e901c7090f 100644 --- a/chore/gentests/gentests.go +++ b/chore/gentests/gentests.go @@ -38,6 +38,7 @@ func main() { llgenDir(dir + "/cl/_testgo") llgenDir(dir + "/cl/_testpy") llgenDir(dir + "/cl/_testdata") + genMetaDir(dir + "/cl/_testmeta") genExpects(dir) } @@ -72,6 +73,34 @@ func genExpects(root string) { runExpectDir(root, "cl/_testdata") } +func genMetaDir(dir string) { + fis, err := os.ReadDir(dir) + check(err) + for _, fi := range fis { + name := fi.Name() + if !fi.IsDir() || strings.HasPrefix(name, "_") { + continue + } + testDir := filepath.Join(dir, name) + if _, err := os.Stat(filepath.Join(testDir, "in.go")); err != nil { + if os.IsNotExist(err) { + continue + } + check(err) + } + relPath, err := filepath.Rel(filepath.Dir(filepath.Dir(dir)), testDir) + check(err) + relPath = filepath.ToSlash(relPath) + fmt.Fprintln(os.Stderr, "meta", relPath) + meta, err := cltest.CaptureMeta("./"+relPath, testDir) + if err != nil { + fmt.Fprintln(os.Stderr, "error:", relPath, err) + continue + } + check(os.WriteFile(filepath.Join(testDir, "meta-expect.txt"), []byte(meta), 0644)) + } +} + func runExpectDir(root, relDir string) { dir := filepath.Join(root, relDir) fis, err := os.ReadDir(dir) diff --git a/cl/_testmeta/ifaceuse_basic/in.go b/cl/_testmeta/ifaceuse_basic/in.go new file mode 100644 index 0000000000..ab6ca52475 --- /dev/null +++ b/cl/_testmeta/ifaceuse_basic/in.go @@ -0,0 +1,11 @@ +package main + +type T struct{} + +func sink(v any) { + _ = v +} + +func main() { + sink(T{}) +} diff --git a/cl/_testmeta/ifaceuse_basic/meta-expect.txt b/cl/_testmeta/ifaceuse_basic/meta-expect.txt new file mode 100644 index 0000000000..27e0e94335 --- /dev/null +++ b/cl/_testmeta/ifaceuse_basic/meta-expect.txt @@ -0,0 +1,4 @@ +[UseIface] +github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T + diff --git a/cl/_testmeta/interface_anyonmous/in.go b/cl/_testmeta/interface_anyonmous/in.go new file mode 100644 index 0000000000..01c93e8514 --- /dev/null +++ b/cl/_testmeta/interface_anyonmous/in.go @@ -0,0 +1,18 @@ +package main + +type T struct{} + +func (T) M() {} +func (T) N() {} + +func use(v interface { + M() + N() +}) { + v.M() + v.N() +} + +func main() { + use(T{}) +} diff --git a/cl/_testmeta/interface_anyonmous/meta-expect.txt b/cl/_testmeta/interface_anyonmous/meta-expect.txt new file mode 100644 index 0000000000..4da791e010 --- /dev/null +++ b/cl/_testmeta/interface_anyonmous/meta-expect.txt @@ -0,0 +1,22 @@ +[InterfaceInfo] +_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.use: + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M + 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M + 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N + diff --git a/cl/_testmeta/interface_named/in.go b/cl/_testmeta/interface_named/in.go new file mode 100644 index 0000000000..dc91d644f0 --- /dev/null +++ b/cl/_testmeta/interface_named/in.go @@ -0,0 +1,17 @@ +package main + +type I interface { + M() +} + +type T struct{} + +func (T) M() {} + +func use(v I) { + v.M() +} + +func main() { + use(T{}) +} diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt new file mode 100644 index 0000000000..75e1423fa2 --- /dev/null +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -0,0 +1,18 @@ +[InterfaceInfo] +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: + M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_named.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_named.use: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_named.T.M + diff --git a/cl/_testmeta/interface_unexported/in.go b/cl/_testmeta/interface_unexported/in.go new file mode 100644 index 0000000000..18778f7891 --- /dev/null +++ b/cl/_testmeta/interface_unexported/in.go @@ -0,0 +1,17 @@ +package main + +type I interface { + m() +} + +type T struct{} + +func (T) m() {} + +func use(v I) { + v.m() +} + +func main() { + use(T{}) +} diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt new file mode 100644 index 0000000000..fc94da51be --- /dev/null +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -0,0 +1,18 @@ +[InterfaceInfo] +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m + diff --git a/cl/_testmeta/methodinfo_imported/in.go b/cl/_testmeta/methodinfo_imported/in.go new file mode 100644 index 0000000000..b243bd18fb --- /dev/null +++ b/cl/_testmeta/methodinfo_imported/in.go @@ -0,0 +1,11 @@ +package main + +import ( + "bytes" + "io" +) + +func main() { + var r io.Reader = bytes.NewBuffer(nil) + _, _ = r.Read(nil) +} diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt new file mode 100644 index 0000000000..d0d5015c62 --- /dev/null +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -0,0 +1,42 @@ +[InterfaceInfo] +_llgo_io.Reader: + Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: + *_llgo_bytes.Buffer + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: + _llgo_io.Reader Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + +[MethodInfo] +*_llgo_bytes.Buffer: + 0 Available _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Available bytes.(*Buffer).Available + 1 AvailableBuffer _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY bytes.(*Buffer).AvailableBuffer bytes.(*Buffer).AvailableBuffer + 2 Bytes _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY bytes.(*Buffer).Bytes bytes.(*Buffer).Bytes + 3 Cap _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Cap bytes.(*Buffer).Cap + 4 Grow _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA bytes.(*Buffer).Grow bytes.(*Buffer).Grow + 5 Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Len bytes.(*Buffer).Len + 6 Next _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY bytes.(*Buffer).Next bytes.(*Buffer).Next + 7 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk bytes.(*Buffer).Read bytes.(*Buffer).Read + 8 ReadByte _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ bytes.(*Buffer).ReadByte bytes.(*Buffer).ReadByte + 9 ReadBytes _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 bytes.(*Buffer).ReadBytes bytes.(*Buffer).ReadBytes + 10 ReadFrom _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 bytes.(*Buffer).ReadFrom bytes.(*Buffer).ReadFrom + 11 ReadRune _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y bytes.(*Buffer).ReadRune bytes.(*Buffer).ReadRune + 12 ReadString _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o bytes.(*Buffer).ReadString bytes.(*Buffer).ReadString + 13 Reset _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac bytes.(*Buffer).Reset bytes.(*Buffer).Reset + 14 String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to bytes.(*Buffer).String bytes.(*Buffer).String + 15 Truncate _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA bytes.(*Buffer).Truncate bytes.(*Buffer).Truncate + 16 UnreadByte _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w bytes.(*Buffer).UnreadByte bytes.(*Buffer).UnreadByte + 17 UnreadRune _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w bytes.(*Buffer).UnreadRune bytes.(*Buffer).UnreadRune + 18 Write _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk bytes.(*Buffer).Write bytes.(*Buffer).Write + 19 WriteByte _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg bytes.(*Buffer).WriteByte bytes.(*Buffer).WriteByte + 20 WriteRune _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw bytes.(*Buffer).WriteRune bytes.(*Buffer).WriteRune + 21 WriteString _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw bytes.(*Buffer).WriteString bytes.(*Buffer).WriteString + 22 WriteTo _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M bytes.(*Buffer).WriteTo bytes.(*Buffer).WriteTo + 23 bytes.empty _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk bytes.(*Buffer).empty bytes.(*Buffer).empty + 24 bytes.grow _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU bytes.(*Buffer).grow bytes.(*Buffer).grow + 25 bytes.readSlice _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 bytes.(*Buffer).readSlice bytes.(*Buffer).readSlice + 26 bytes.tryGrowByReslice _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M bytes.(*Buffer).tryGrowByReslice bytes.(*Buffer).tryGrowByReslice + diff --git a/cl/_testmeta/reflect_dynamic/in.go b/cl/_testmeta/reflect_dynamic/in.go new file mode 100644 index 0000000000..15ed1f1a5f --- /dev/null +++ b/cl/_testmeta/reflect_dynamic/in.go @@ -0,0 +1,15 @@ +package main + +import "reflect" + +type T struct{} + +func (T) M() {} + +func use(name string) { + _ = reflect.ValueOf(T{}).MethodByName(name) +} + +func main() { + use("M") +} diff --git a/cl/_testmeta/reflect_dynamic/meta-expect.txt b/cl/_testmeta/reflect_dynamic/meta-expect.txt new file mode 100644 index 0000000000..b0c468ca28 --- /dev/null +++ b/cl/_testmeta/reflect_dynamic/meta-expect.txt @@ -0,0 +1,13 @@ +[UseIface] +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M +_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M + +[ReflectMethod] +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use + diff --git a/cl/_testmeta/reflect_named/in.go b/cl/_testmeta/reflect_named/in.go new file mode 100644 index 0000000000..28c989074d --- /dev/null +++ b/cl/_testmeta/reflect_named/in.go @@ -0,0 +1,13 @@ +package main + +import "reflect" + +type T struct{} + +func (T) M() {} +func (T) m() {} + +func main() { + _, _ = reflect.TypeOf(T{}).MethodByName("M") + _, _ = reflect.TypeOf(T{}).MethodByName("m") +} diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt new file mode 100644 index 0000000000..bb3c6b23fc --- /dev/null +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -0,0 +1,61 @@ +[InterfaceInfo] +_llgo_reflect.Type: + Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + AssignableTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Bits _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + CanSeq _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + CanSeq2 _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ChanDir _llgo_func$JO3khPIbANSMBmoN6P7ybYAeUBd3Gv6toVUqNeE7qbE + Comparable _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ConvertibleTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Elem _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Field _llgo_func$Q3NYrysaKgu1MtMuLQwb-k5QcKGHihnt-tV_NlNJQFA + FieldAlign _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + FieldByIndex _llgo_func$LPPtiM49dEPl48CC3WRhXm3YPnfUJEZE_k8Tx3rMuSk + FieldByName _llgo_func$dEvABJ5r0MMUlf4smWpIDG5dO8AuGklGdNJ1xneL3UM + FieldByNameFunc _llgo_func$xySrXVFC_2LK2oP71R2UryKi6UmdEJUo9k6aQuz4TvI + Implements _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + In _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + IsVariadic _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + Key _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Kind _llgo_func$w8Mj2LK8G5p7MIiGWR6MYjyXy3L8SVVzYlT1bb6KNXk + Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Method _llgo_func$FmJJGomlX5kINJGxQdQDCAkD89ySoMslAYFrziWInVc + MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + Name _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + NumField _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumIn _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumMethod _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumOut _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Out _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + OverflowComplex _llgo_func$cGkbH-2LQOLoq64Rqj3WeO56U8al7FfVkf5K1FFbPpE + OverflowFloat _llgo_func$uk7PgUVap9GZdvS8R_mZCDbAbqnAbcNryqybtDogUNI + OverflowInt _llgo_func$odFOIClZoEVGbTP_BEfZxVM5ex3r8Fj1afUEeP_awp8 + OverflowUint _llgo_func$7I97sofX8UqJA96mVIy89KPUfSM_efkrR-mJQ9qaHfk + PkgPath _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + Size _llgo_func$1kITCsyu7hFLMxHLR7kDlvu4SOra_HtrtdFUQH9P13s + String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + reflect.common _llgo_func$w6XuV-1SmW103DbauPseXBpW50HpxXAEsUsGFibl0Uw + reflect.uncommon _llgo_func$iG49bujiXjI2lVflYdE0hPXlCAABL-XKRANSNJEKOio + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/reflect_named.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/reflect_named.main: + _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M + 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m +_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M + 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m + +[UseNamedMethod] +github.com/goplus/llgo/cl/_testmeta/reflect_named.main: + M + m + diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 1087d66251..48f9c8c660 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -39,6 +39,7 @@ import ( "github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/littest" "github.com/goplus/llgo/internal/llgen" + "github.com/goplus/llgo/internal/metadata" "github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/ssa/ssatest" "github.com/qiniu/x/test" @@ -66,6 +67,7 @@ type runOptions struct { filter func(string) string checkIR bool checkOutput bool + checkMeta bool } // RunOption customizes directory-based test behavior. @@ -99,6 +101,13 @@ func WithIRCheck(enabled bool) RunOption { } } +// WithMetaCheck enables or disables package metadata golden checks. +func WithMetaCheck(enabled bool) RunOption { + return func(opts *runOptions) { + opts.checkMeta = enabled + } +} + // FilterEmulatorOutput strips emulator boot logs by returning output after "entry 0x...". func FilterEmulatorOutput(output string) string { output = strings.ReplaceAll(output, "\r\n", "\n") @@ -243,12 +252,22 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption if opts.checkIR { testFrom(t, pkgDir, sel) } + if opts.checkMeta { + conf, _, capturedMeta := withModuleCapture(opts.conf, pkgDir) + output, err := runWithConf(relPkg, pkgDir, conf) + if err != nil { + t.Logf("raw output:\n%s", string(output)) + t.Fatalf("run failed: %v\noutput: %s", err, string(output)) + } + assertExpectedMeta(t, pkgDir, relPkg, capturedMeta) + } return } var irSpec littest.Spec conf := opts.conf var capturedIR *string + var capturedMeta *string var checkIR bool if opts.checkIR { irSpec, checkIR, err = readIRSpec(pkgDir) @@ -256,9 +275,12 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption t.Fatal("LoadSpec failed:", err) } if checkIR { - conf, capturedIR = withModuleCapture(opts.conf, pkgDir) + conf, capturedIR, capturedMeta = withModuleCapture(opts.conf, pkgDir) } } + if opts.checkMeta && capturedMeta == nil { + conf, capturedIR, capturedMeta = withModuleCapture(conf, pkgDir) + } output, err := runWithConf(relPkg, pkgDir, conf) if err != nil { @@ -267,6 +289,9 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption } assertExpectedOutput(t, pkgDir, expectedOutput, output, opts) + if opts.checkMeta { + assertExpectedMeta(t, pkgDir, relPkg, capturedMeta) + } if !checkIR { return } @@ -279,22 +304,50 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption } } +func assertExpectedMeta(t *testing.T, pkgDir, relPkg string, capturedMeta *string) { + t.Helper() + expectedMeta, hasMeta, err := readGolden(filepath.Join(pkgDir, "meta-expect.txt")) + if err != nil { + t.Fatal("ReadFile failed:", err) + } + if !hasMeta { + t.Fatal("missing meta-expect.txt") + } + if capturedMeta == nil { + t.Fatalf("metadata snapshot missing for %s", relPkg) + } + if test.Diff(t, filepath.Join(pkgDir, "meta-expect.txt.new"), []byte(*capturedMeta), expectedMeta) { + t.Fatal("metadata: unexpected result") + } +} + func RunAndCapture(relPkg, pkgDir string) ([]byte, error) { conf := build.NewDefaultConf(build.ModeRun) return RunAndCaptureWithConf(relPkg, pkgDir, conf) } +// CaptureMeta builds relPkg and returns the package metadata captured for pkgDir. +func CaptureMeta(relPkg, pkgDir string) (string, error) { + conf, _, capturedMeta := withModuleCapture(build.NewDefaultConf(build.ModeRun), pkgDir) + output, err := runWithConf(relPkg, pkgDir, conf) + if err != nil { + return "", fmt.Errorf("%w\noutput: %s", err, string(output)) + } + return *capturedMeta, nil +} + // RunAndCaptureWithConf runs llgo with a custom build config and captures output. func RunAndCaptureWithConf(relPkg, pkgDir string, conf *build.Config) ([]byte, error) { return runWithConf(relPkg, pkgDir, conf) } -func withModuleCapture(conf *build.Config, pkgDir string) (*build.Config, *string) { +func withModuleCapture(conf *build.Config, pkgDir string) (*build.Config, *string, *string) { if conf == nil { conf = build.NewDefaultConf(build.ModeRun) } localConf := *conf var module string + var meta string prevHook := localConf.ModuleHook localConf.ModuleHook = func(pkg build.Package) { if prevHook != nil { @@ -304,9 +357,10 @@ func withModuleCapture(conf *build.Config, pkgDir string) (*build.Config, *strin return filepath.Dir(file) == pkgDir }) { module = pkg.LPkg.String() + meta = metadata.MetaString(pkg.Meta) } } - return &localConf, &module + return &localConf, &module, &meta } func runWithConf(relPkg, pkgDir string, conf *build.Config) ([]byte, error) { diff --git a/cl/compile.go b/cl/compile.go index bc073c23de..b215ac6c1f 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -31,6 +31,7 @@ import ( "github.com/goplus/llgo/cl/blocks" "github.com/goplus/llgo/internal/goembed" + "github.com/goplus/llgo/internal/metadata" "github.com/goplus/llgo/internal/typepatch" "golang.org/x/tools/go/ssa" @@ -1407,17 +1408,17 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll // The rewrites map uses short variable names (without package qualifier) and // only affects string-typed globals defined in the current package. func NewPackageEx(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs []string, err error) { - return newPackageEx(prog, patches, rewrites, pkg, files, nil) + return newPackageEx(prog, patches, rewrites, pkg, files, nil, nil) } // NewPackageExWithEmbed compiles a package using pre-loaded go:embed metadata. // // This avoids re-scanning directives when the caller already loaded them. -func NewPackageExWithEmbed(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap goembed.VarMap) (ret llssa.Package, externs []string, err error) { - return newPackageEx(prog, patches, rewrites, pkg, files, &embedMap) +func NewPackageExWithEmbed(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap goembed.VarMap, metaBuilder *metadata.Builder) (ret llssa.Package, externs []string, err error) { + return newPackageEx(prog, patches, rewrites, pkg, files, &embedMap, metaBuilder) } -func newPackageEx(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap *goembed.VarMap) (ret llssa.Package, externs []string, err error) { +func newPackageEx(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap *goembed.VarMap, metaBuilder *metadata.Builder) (ret llssa.Package, externs []string, err error) { pkgProg := pkg.Prog pkgTypes := pkg.Pkg oldTypes := pkgTypes @@ -1432,6 +1433,7 @@ func newPackageEx(prog llssa.Program, patches Patches, rewrites map[string]strin prog.SetRuntime(pkgTypes) } ret = prog.NewPackage(pkgName, pkgPath) + ret.MetaBuilder = metaBuilder if enableDbg { ret.InitDebug(pkgName, pkgPath, pkgProg.Fset) } diff --git a/cl/compile_test.go b/cl/compile_test.go index a7eed9ccd8..4eaac60863 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -173,6 +173,14 @@ func TestRunAndTestFromTestgo(t *testing.T) { cltest.RunAndTestFromDir(t, "", "./_testgo", nil) } +func TestRunAndTestFromTestmeta(t *testing.T) { + cltest.RunAndTestFromDir(t, "", "./_testmeta", nil, + cltest.WithOutputCheck(false), + cltest.WithIRCheck(false), + cltest.WithMetaCheck(true), + ) +} + func TestFilterEmulatorOutput(t *testing.T) { tests := []struct { name string diff --git a/cl/embed_with_map_compile_test.go b/cl/embed_with_map_compile_test.go index 069be194d1..203a07fa25 100644 --- a/cl/embed_with_map_compile_test.go +++ b/cl/embed_with_map_compile_test.go @@ -57,7 +57,7 @@ var files embed.FS prog := ssatest.NewProgramEx(t, nil, imp) prog.TypeSizes(types.SizesFor("gc", runtime.GOARCH)) - ret, _, err := cl.NewPackageExWithEmbed(prog, nil, nil, fooPkg, files, embedMap) + ret, _, err := cl.NewPackageExWithEmbed(prog, nil, nil, fooPkg, files, embedMap, nil) if err != nil { t.Fatalf("NewPackageExWithEmbed failed: %v", err) } diff --git a/cl/instr.go b/cl/instr.go index 31ac30788e..be122cec11 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -26,6 +26,7 @@ import ( "regexp" "strings" + "github.com/goplus/llgo/internal/metadata" "golang.org/x/tools/go/ssa" llssa "github.com/goplus/llgo/ssa" @@ -53,6 +54,65 @@ func constBool(v ssa.Value) (ret bool, ok bool) { return } +func isNamedType(t types.Type, pkgPath, name string) bool { + named, ok := types.Unalias(t).(*types.Named) + if !ok { + return false + } + obj := named.Obj() + return obj != nil && obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == pkgPath +} + +func staticCallMethod(call *ssa.CallCommon) (recv types.Type, method string, ok bool) { + fn := call.StaticCallee() + if fn == nil || fn.Signature == nil || fn.Signature.Recv() == nil { + return nil, "", false + } + return fn.Signature.Recv().Type(), fn.Name(), true +} + +func reflectMethodNameArg(call *ssa.CallCommon) (nameArg ssa.Value, ok bool) { + if method := call.Method; method != nil { + if !isNamedType(call.Value.Type(), "reflect", "Type") { + return nil, false + } + if method.Name() != "MethodByName" { + return nil, method.Name() == "Method" + } + return call.Args[0], true + } + + recv, methodName, ok := staticCallMethod(call) + if !ok || !isNamedType(recv, "reflect", "Value") { + return nil, false + } + if methodName != "MethodByName" { + return nil, methodName == "Method" + } + return call.Args[len(call.Args)-1], true +} + +func (p *context) markReflectMethodCall(call *ssa.CallCommon) { + if p.fn == nil || p.pkg.MetaBuilder == nil { + return + } + nameArg, ok := reflectMethodNameArg(call) + if !ok { + return + } + mb := p.pkg.MetaBuilder + owner := mb.Symbol(p.fn.Name()) + if nameArg == nil { + mb.AddReflectMethod(owner) + return + } + if name, ok := constStr(nameArg); ok { + mb.AddUseNamedMethod(owner, []metadata.Name{mb.Name(name)}) + return + } + mb.AddReflectMethod(owner) +} + // func pystr(string) *py.Object func pystr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) { if len(args) == 1 { @@ -839,6 +899,7 @@ func (p *context) emitDo(b llssa.Builder, act llssa.DoAction, ds *explicitDeferS } func (p *context) callEx(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon, ds *explicitDeferStack) (ret llssa.Expr) { + p.markReflectMethodCall(call) cv := call.Value if mthd := call.Method; mthd != nil { o := p.compileValue(b, cv) diff --git a/internal/build/build.go b/internal/build/build.go index 09f6c310e8..047bc19504 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1268,12 +1268,16 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { return fmt.Errorf("load go:embed directives for %s failed: %w", pkgPath, err) } - ret, externs, err := cl.NewPackageExWithEmbed(ctx.prog, ctx.patches, aPkg.rewriteVars, aPkg.SSA, syntax, embedMap) + var metaBuilder *metadata.Builder + if !aPkg.CacheHit { + metaBuilder = metadata.NewBuilder() + } + ret, externs, err := cl.NewPackageExWithEmbed(ctx.prog, ctx.patches, aPkg.rewriteVars, aPkg.SSA, syntax, embedMap, metaBuilder) check(err) aPkg.LPkg = ret - if !aPkg.CacheHit && aPkg.Meta == nil { - aPkg.Meta = metadata.NewBuilder().Build() + if metaBuilder != nil { + aPkg.Meta = metaBuilder.Build() } if hook := ctx.buildConf.ModuleHook; hook != nil { hook(aPkg) diff --git a/internal/metadata/builder.go b/internal/metadata/builder.go index 74ca47d96f..4363486118 100644 --- a/internal/metadata/builder.go +++ b/internal/metadata/builder.go @@ -35,37 +35,47 @@ func (b *Builder) intern(s string) uint32 { // AddEdge records an ordinary reachability edge src -> dst. func (b *Builder) AddEdge(src, dst Symbol) { - b.pm.ordinaryEdges[src] = append(b.pm.ordinaryEdges[src], dst) + b.pm.ordinaryEdges[src] = appendSymbolUnique(b.pm.ordinaryEdges[src], dst) } // AddTypeChild records that parent type references child type. func (b *Builder) AddTypeChild(parent, child Symbol) { - b.pm.typeChildren[parent] = append(b.pm.typeChildren[parent], child) + b.pm.typeChildren[parent] = appendSymbolUnique(b.pm.typeChildren[parent], child) } // AddIfaceEntry records the method set of an interface type. func (b *Builder) AddIfaceEntry(iface Symbol, methods []MethodSig) { - b.pm.interfaceInfo[iface] = append(b.pm.interfaceInfo[iface], methods...) + for _, method := range methods { + b.pm.interfaceInfo[iface] = appendMethodSigUnique(b.pm.interfaceInfo[iface], method) + } } // AddUseIface records types converted to interface when owner is reachable. func (b *Builder) AddUseIface(owner Symbol, types []Symbol) { - b.pm.useIface[owner] = append(b.pm.useIface[owner], types...) + for _, typ := range types { + b.pm.useIface[owner] = appendSymbolUnique(b.pm.useIface[owner], typ) + } } // AddUseIfaceMethod records interface method calls when owner is reachable. func (b *Builder) AddUseIfaceMethod(owner Symbol, demands []IfaceMethodDemand) { - b.pm.useIfaceMethod[owner] = append(b.pm.useIfaceMethod[owner], demands...) + for _, demand := range demands { + b.pm.useIfaceMethod[owner] = appendIfaceMethodDemandUnique(b.pm.useIfaceMethod[owner], demand) + } } // AddMethodInfo records concrete type method table slots. func (b *Builder) AddMethodInfo(typeID Symbol, slots []MethodSlot) { - b.pm.methodInfo[typeID] = append(b.pm.methodInfo[typeID], slots...) + for _, slot := range slots { + b.pm.methodInfo[typeID] = appendMethodSlotUnique(b.pm.methodInfo[typeID], slot) + } } // AddUseNamedMethod records constant MethodByName method names. func (b *Builder) AddUseNamedMethod(owner Symbol, names []Name) { - b.pm.useNamedMethod[owner] = append(b.pm.useNamedMethod[owner], names...) + for _, name := range names { + b.pm.useNamedMethod[owner] = appendNameUnique(b.pm.useNamedMethod[owner], name) + } } // AddReflectMethod records that owner triggers conservative reflection handling. @@ -82,3 +92,48 @@ func (b *Builder) Build() *PackageMeta { b.pm.stringTable = table return b.pm } + +func appendSymbolUnique(items []Symbol, item Symbol) []Symbol { + for _, existing := range items { + if existing == item { + return items + } + } + return append(items, item) +} + +func appendNameUnique(items []Name, item Name) []Name { + for _, existing := range items { + if existing == item { + return items + } + } + return append(items, item) +} + +func appendMethodSigUnique(items []MethodSig, item MethodSig) []MethodSig { + for _, existing := range items { + if existing == item { + return items + } + } + return append(items, item) +} + +func appendIfaceMethodDemandUnique(items []IfaceMethodDemand, item IfaceMethodDemand) []IfaceMethodDemand { + for _, existing := range items { + if existing == item { + return items + } + } + return append(items, item) +} + +func appendMethodSlotUnique(items []MethodSlot, item MethodSlot) []MethodSlot { + for _, existing := range items { + if existing == item { + return items + } + } + return append(items, item) +} diff --git a/ssa/abitype.go b/ssa/abitype.go index 37a0d7058b..b9468dc48f 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -26,6 +26,8 @@ import ( "github.com/goplus/llgo/ssa/abi" "github.com/xgo-dev/llvm" + + "github.com/goplus/llgo/internal/metadata" ) // ----------------------------------------------------------------------------- @@ -424,6 +426,11 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va ft := prog.rtType("Method") n := mset.Len() fields := make([]llvm.Value, n) + var slots []metadata.MethodSlot + typeName, _ := prog.abi.TypeName(t) + if b.Pkg.MetaBuilder != nil { + slots = make([]metadata.MethodSlot, 0, n) + } pkg, _ := b.abiUncommonPkg(t) anonymous := pkg == nil if anonymous { @@ -431,11 +438,12 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va } for i := 0; i < n; i++ { m := mset.At(i) - obj := m.Obj() + obj := m.Obj().(*types.Func) mName := obj.Name() + fullName := mthName(obj) name := b.Str(mName).impl if !token.IsExported(mName) { - name = b.Str(abi.FullName(obj.Pkg(), mName)).impl + name = b.Str(fullName).impl } mSig := m.Type().(*types.Signature) var tfn, ifn llvm.Value @@ -453,6 +461,20 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va values = append(values, ifn) values = append(values, tfn) fields[i] = llvm.ConstNamedStruct(ft.ll, values) + if mb := b.Pkg.MetaBuilder; mb != nil { + mtypeName, _ := prog.abi.TypeName(ftyp) + slots = append(slots, metadata.MethodSlot{ + Sig: metadata.MethodSig{ + Name: mb.Name(fullName), + MType: mb.Symbol(mtypeName), + }, + IFn: mb.Symbol(ifn.Name()), + TFn: mb.Symbol(tfn.Name()), + }) + } + } + if mb := b.Pkg.MetaBuilder; mb != nil { + mb.AddMethodInfo(mb.Symbol(typeName), slots) } return llvm.ConstArray(ft.ll, fields) } @@ -463,6 +485,14 @@ func funcType(prog Program, typ types.Type) types.Type { return ftyp.raw.Type.(*types.Struct).Field(0).Type() } +func mthName(method *types.Func) string { + name := method.Name() + if token.IsExported(name) { + return name + } + return abi.FullName(method.Pkg(), name) +} + func (b Builder) abiMethodFunc(anonymous bool, mPkg *types.Package, mName string, mSig *types.Signature) (tfn llvm.Value) { var fullName string if anonymous { diff --git a/ssa/expr.go b/ssa/expr.go index 6bf4e5cfb2..90ece978c6 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -24,6 +24,7 @@ import ( "go/types" "log" + "github.com/goplus/llgo/internal/metadata" "github.com/xgo-dev/llvm" ) @@ -1186,9 +1187,15 @@ func (b Builder) checkReflect(fn Expr, args []Expr) { } pkg.MethodByIndex[v] = none{} pkg.NeedAbiInit |= ReflectMethodByIndex + if mb := pkg.MetaBuilder; mb != nil { + mb.AddReflectMethod(mb.Symbol(b.Func.Name())) + } return } pkg.NeedAbiInit |= ReflectMethodDynamic + if mb := pkg.MetaBuilder; mb != nil { + mb.AddReflectMethod(mb.Symbol(b.Func.Name())) + } } case "reflect.Value.MethodByName": if len(args) == 2 { @@ -1198,9 +1205,15 @@ func (b Builder) checkReflect(fn Expr, args []Expr) { } pkg.MethodByName[v] = none{} pkg.NeedAbiInit |= ReflectMethodByName + if mb := pkg.MetaBuilder; mb != nil { + mb.AddUseNamedMethod(mb.Symbol(b.Func.Name()), []metadata.Name{mb.Name(v)}) + } return } pkg.NeedAbiInit |= ReflectMethodDynamic + if mb := pkg.MetaBuilder; mb != nil { + mb.AddReflectMethod(mb.Symbol(b.Func.Name())) + } } } } diff --git a/ssa/interface.go b/ssa/interface.go index aceb775a75..90df94fac2 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -20,6 +20,7 @@ import ( "go/token" "go/types" + "github.com/goplus/llgo/internal/metadata" "github.com/goplus/llgo/ssa/abi" "github.com/xgo-dev/llvm" ) @@ -80,6 +81,28 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { } tclosure := prog.Type(sig, InGo) i := iMethodOf(rawIntf, method.Name()) + if mb := b.Pkg.MetaBuilder; mb != nil { + intfTypeName, _ := prog.abi.TypeName(intf.raw.Type) + mtypeName, _ := prog.abi.TypeName(funcType(prog, method.Type())) + mb.AddUseIfaceMethod(mb.Symbol(b.Func.Name()), []metadata.IfaceMethodDemand{{ + Target: mb.Symbol(intfTypeName), + Sig: metadata.MethodSig{ + Name: mb.Name(mthName(method)), + MType: mb.Symbol(mtypeName), + }, + }}) + + methods := make([]metadata.MethodSig, 0, rawIntf.NumMethods()) + for i := 0; i < rawIntf.NumMethods(); i++ { + im := rawIntf.Method(i) + imtypeName, _ := prog.abi.TypeName(funcType(prog, im.Type())) + methods = append(methods, metadata.MethodSig{ + Name: mb.Name(mthName(im)), + MType: mb.Symbol(imtypeName), + }) + } + mb.AddIfaceEntry(mb.Symbol(intfTypeName), methods) + } data := b.InlineCall(b.Pkg.rtFunc("IfacePtrData"), intf) impl := intf.impl itab := Expr{b.faceItab(impl), prog.VoidPtrPtr()} @@ -114,6 +137,12 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { } prog := b.Prog typ := x.Type + if mb := b.Pkg.MetaBuilder; mb != nil { + if _, ok := types.Unalias(typ.raw.Type).Underlying().(*types.Interface); !ok { + typeName, _ := prog.abi.TypeName(typ.raw.Type) + mb.AddUseIface(mb.Symbol(b.Func.Name()), []metadata.Symbol{mb.Symbol(typeName)}) + } + } tabi := b.abiType(typ.raw.Type) if !directIfaceType(typ.raw.Type) { vptr := b.AllocU(typ) diff --git a/ssa/package.go b/ssa/package.go index 0db719c157..28ba9fdf5d 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -26,6 +26,7 @@ import ( "unsafe" "github.com/goplus/llgo/internal/env" + "github.com/goplus/llgo/internal/metadata" "github.com/goplus/llgo/ssa/abi" "github.com/xgo-dev/llvm" "golang.org/x/tools/go/types/typeutil" @@ -732,6 +733,7 @@ type aPackage struct { NeedAbiInit int // bitmask of Reflect* flags indicating which reflect type-construction operations are used MethodByIndex map[int]none MethodByName map[string]none + MetaBuilder *metadata.Builder export map[string]string // pkgPath.nameInPkg => exportname preserveSyms map[string]struct{} // set of exported symbol names From 4e788ff67392872bb936a78e3132502032b3ea9e Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 17:00:43 +0800 Subject: [PATCH 04/37] wip: emit type children metadata --- cl/_testmeta/ifaceuse_basic/meta-expect.txt | 4 + .../interface_anyonmous/meta-expect.txt | 10 ++ cl/_testmeta/interface_named/meta-expect.txt | 10 ++ .../interface_unexported/meta-expect.txt | 10 ++ .../methodinfo_imported/meta-expect.txt | 140 ++++++++++++++++++ cl/_testmeta/reflect_dynamic/meta-expect.txt | 6 + cl/_testmeta/reflect_named/meta-expect.txt | 6 + cl/_testmeta/typechildren_basic/in.go | 16 ++ .../typechildren_basic/meta-expect.txt | 19 +++ ssa/abitype.go | 66 +++++++++ 10 files changed, 287 insertions(+) create mode 100644 cl/_testmeta/typechildren_basic/in.go create mode 100644 cl/_testmeta/typechildren_basic/meta-expect.txt diff --git a/cl/_testmeta/ifaceuse_basic/meta-expect.txt b/cl/_testmeta/ifaceuse_basic/meta-expect.txt index 27e0e94335..0b0f342c6d 100644 --- a/cl/_testmeta/ifaceuse_basic/meta-expect.txt +++ b/cl/_testmeta/ifaceuse_basic/meta-expect.txt @@ -1,3 +1,7 @@ +[TypeChildren] +*_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T + [UseIface] github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.main: _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T diff --git a/cl/_testmeta/interface_anyonmous/meta-expect.txt b/cl/_testmeta/interface_anyonmous/meta-expect.txt index 4da791e010..8a9752a1aa 100644 --- a/cl/_testmeta/interface_anyonmous/meta-expect.txt +++ b/cl/_testmeta/interface_anyonmous/meta-expect.txt @@ -1,3 +1,13 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T +*_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo +_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + [InterfaceInfo] _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index 75e1423fa2..3ccf78b8bb 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -1,3 +1,13 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T +*_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + [InterfaceInfo] _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index fc94da51be..9163d29552 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -1,3 +1,13 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T +*github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + [InterfaceInfo] _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index d0d5015c62..e7bf4eefa2 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -1,3 +1,143 @@ +[TypeChildren] +*[]_llgo_uint8: + []_llgo_uint8 +*_llgo_bool: + _llgo_bool +*_llgo_bytes.Buffer: + _llgo_bytes.Buffer +*_llgo_bytes.readOp: + _llgo_bytes.readOp +*_llgo_error: + _llgo_error +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +*_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: + _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o +*_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: + _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA +*_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: + _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk +*_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY: + _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY +*_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0: + _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 +*_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY: + _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY +*_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU: + _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU +*_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: + _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ +*_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: + _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y +*_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: + _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M +*_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: + _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw +*_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: + _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 +*_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: + _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw +*_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: + _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M +*_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: + _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw +*_llgo_int: + _llgo_int +*_llgo_int32: + _llgo_int32 +*_llgo_int64: + _llgo_int64 +*_llgo_io.Reader: + _llgo_io.Reader +*_llgo_io.Writer: + _llgo_io.Writer +*_llgo_string: + _llgo_string +*_llgo_uint8: + _llgo_uint8 +[]_llgo_uint8: + _llgo_uint8 +_llgo_bytes.Buffer: + []_llgo_uint8 + _llgo_int + _llgo_bytes.readOp +_llgo_error: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + _llgo_error +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_int +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + []_llgo_uint8 + _llgo_int + _llgo_error +_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: + _llgo_uint8 + _llgo_string + _llgo_error +_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: + _llgo_int +_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: + _llgo_bool +_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY: + []_llgo_uint8 +_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0: + _llgo_uint8 + []_llgo_uint8 + _llgo_error +_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY: + _llgo_int + []_llgo_uint8 +_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU: + _llgo_int +_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: + _llgo_uint8 + _llgo_error +_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: + _llgo_int32 + _llgo_int + _llgo_error +_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: + _llgo_int + _llgo_bool +_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: + _llgo_string + _llgo_int + _llgo_error +_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: + _llgo_io.Reader + _llgo_int64 + _llgo_error +_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: + _llgo_int32 + _llgo_int + _llgo_error +_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: + _llgo_io.Writer + _llgo_int64 + _llgo_error +_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: + _llgo_uint8 + _llgo_error +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_string +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +_llgo_io.Reader: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +_llgo_io.Writer: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + [InterfaceInfo] _llgo_io.Reader: Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk diff --git a/cl/_testmeta/reflect_dynamic/meta-expect.txt b/cl/_testmeta/reflect_dynamic/meta-expect.txt index b0c468ca28..70aadcb457 100644 --- a/cl/_testmeta/reflect_dynamic/meta-expect.txt +++ b/cl/_testmeta/reflect_dynamic/meta-expect.txt @@ -1,3 +1,9 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T + [UseIface] github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index bb3c6b23fc..3071415cbc 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -1,3 +1,9 @@ +[TypeChildren] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + [InterfaceInfo] _llgo_reflect.Type: Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA diff --git a/cl/_testmeta/typechildren_basic/in.go b/cl/_testmeta/typechildren_basic/in.go new file mode 100644 index 0000000000..03536eead9 --- /dev/null +++ b/cl/_testmeta/typechildren_basic/in.go @@ -0,0 +1,16 @@ +package main + +type Inner struct { + S string +} + +type Outer struct { + I Inner + N int +} + +func use(any) {} + +func main() { + use(Outer{}) +} diff --git a/cl/_testmeta/typechildren_basic/meta-expect.txt b/cl/_testmeta/typechildren_basic/meta-expect.txt new file mode 100644 index 0000000000..dceb1b2067 --- /dev/null +++ b/cl/_testmeta/typechildren_basic/meta-expect.txt @@ -0,0 +1,19 @@ +[TypeChildren] +*_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner +*_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer +*_llgo_int: + _llgo_int +*_llgo_string: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner + _llgo_int + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/typechildren_basic.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer + diff --git a/ssa/abitype.go b/ssa/abitype.go index b9468dc48f..5c4c28396b 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -345,6 +345,71 @@ func (b Builder) abiExtendedFields(t types.Type, name string) (fields []llvm.Val return } +func (b Builder) recordTypeChildren(parentName string, t types.Type) { + mb := b.Pkg.MetaBuilder + if mb == nil { + return + } + parent := mb.Symbol(parentName) + for _, child := range b.directTypeChildren(t) { + childName, _ := b.Prog.abi.TypeName(child) + mb.AddTypeChild(parent, mb.Symbol(childName)) + } +} + +func (b Builder) directTypeChildren(t types.Type) []types.Type { + switch t := types.Unalias(t).(type) { + case *types.Basic: + return nil + case *types.Pointer: + return []types.Type{abi.PublicType(t.Elem())} + case *types.Chan: + return []types.Type{abi.PublicType(t.Elem())} + case *types.Slice: + return []types.Type{abi.PublicType(t.Elem())} + case *types.Array: + elem := abi.PublicType(t.Elem()) + return []types.Type{elem, types.NewSlice(elem)} + case *types.Map: + return []types.Type{ + abi.PublicType(t.Key()), + abi.PublicType(t.Elem()), + // Map type descriptors reference their runtime bucket type too. + b.Prog.abi.MapBucket(t), + } + case *types.Signature: + var children []types.Type + children = appendTupleTypeChildren(children, t.Params()) + children = appendTupleTypeChildren(children, t.Results()) + return children + case *types.Struct: + children := make([]types.Type, 0, t.NumFields()) + for i := 0; i < t.NumFields(); i++ { + children = append(children, abi.PublicType(t.Field(i).Type())) + } + return children + case *types.Interface: + children := make([]types.Type, 0, t.NumMethods()) + for i := 0; i < t.NumMethods(); i++ { + children = append(children, funcType(b.Prog, t.Method(i).Type())) + } + return children + case *types.Named: + return b.directTypeChildren(t.Underlying()) + } + return nil +} + +func appendTupleTypeChildren(children []types.Type, tuple *types.Tuple) []types.Type { + if tuple == nil { + return children + } + for i := 0; i < tuple.Len(); i++ { + children = append(children, abi.PublicType(tuple.At(i).Type())) + } + return children +} + func (b Builder) abiUncommonPkg(t types.Type) (*types.Package, string) { retry: switch typ := types.Unalias(t).(type) { @@ -527,6 +592,7 @@ func (b Builder) abiType(t types.Type) Expr { if prog.patchType != nil { t = prog.patchType(t) } + b.recordTypeChildren(name, t) mset, hasUncommon := b.abiUncommonMethodSet(t) rt := prog.rtNamed(prog.abi.RuntimeName(t)) var typ types.Type = rt From ba878540b3428b20e0c995fdd22fadd21daa4c3f Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 17:19:54 +0800 Subject: [PATCH 05/37] wip: extract ordinary metadata edges --- cl/_testmeta/ifaceuse_basic/meta-expect.txt | 18 + .../interface_anyonmous/meta-expect.txt | 42 +++ cl/_testmeta/interface_named/meta-expect.txt | 40 +++ .../interface_unexported/meta-expect.txt | 40 +++ .../methodinfo_imported/meta-expect.txt | 317 ++++++++++++++++++ cl/_testmeta/reflect_dynamic/meta-expect.txt | 29 ++ cl/_testmeta/reflect_named/meta-expect.txt | 29 ++ .../typechildren_basic/meta-expect.txt | 45 +++ internal/build/build.go | 1 + internal/build/metadata_edges.go | 133 ++++++++ internal/build/metadata_edges_test.go | 106 ++++++ 11 files changed, 800 insertions(+) create mode 100644 internal/build/metadata_edges.go create mode 100644 internal/build/metadata_edges_test.go diff --git a/cl/_testmeta/ifaceuse_basic/meta-expect.txt b/cl/_testmeta/ifaceuse_basic/meta-expect.txt index 0b0f342c6d..6f5a9b9355 100644 --- a/cl/_testmeta/ifaceuse_basic/meta-expect.txt +++ b/cl/_testmeta/ifaceuse_basic/meta-expect.txt @@ -2,6 +2,24 @@ *_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T: _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T +[OrdinaryEdges] +*_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 + *_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T +github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.init: + github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.init$guard +github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.main: + github.com/goplus/llgo/runtime/internal/runtime.AllocU + _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T + github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.sink + [UseIface] github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.main: _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T diff --git a/cl/_testmeta/interface_anyonmous/meta-expect.txt b/cl/_testmeta/interface_anyonmous/meta-expect.txt index 8a9752a1aa..a915573fce 100644 --- a/cl/_testmeta/interface_anyonmous/meta-expect.txt +++ b/cl/_testmeta/interface_anyonmous/meta-expect.txt @@ -13,6 +13,48 @@ _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T +*_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T +_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + *_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo$imethods +_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.init: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.init$guard +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.main: + github.com/goplus/llgo/runtime/internal/runtime.AllocU + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T + github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.use +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.use: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + [UseIface] github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.main: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index 3ccf78b8bb..b341d5d3c4 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -12,6 +12,46 @@ _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T +*_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + *_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: + github.com/goplus/llgo/cl/_testmeta/interface_named.T.M +github.com/goplus/llgo/cl/_testmeta/interface_named.init: + github.com/goplus/llgo/cl/_testmeta/interface_named.init$guard +github.com/goplus/llgo/cl/_testmeta/interface_named.main: + github.com/goplus/llgo/runtime/internal/runtime.AllocU + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T + github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/cl/_testmeta/interface_named.use +github.com/goplus/llgo/cl/_testmeta/interface_named.use: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + [UseIface] github.com/goplus/llgo/cl/_testmeta/interface_named.main: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index 9163d29552..5331fd5455 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -12,6 +12,46 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qA _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T +*github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T +github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + *github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +github.com/goplus/llgo/cl/_testmeta/interface_unexported.init: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.init$guard +github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T + github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/cl/_testmeta/interface_unexported.use +github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + [UseIface] github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index e7bf4eefa2..0f4458a5b0 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -142,6 +142,323 @@ _llgo_io.Writer: _llgo_io.Reader: Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +[OrdinaryEdges] +*[]_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + []_llgo_uint8 +*_llgo_bool: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_bool +*_llgo_bytes.Buffer: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_bytes.Buffer +*_llgo_bytes.readOp: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_bytes.readOp +*_llgo_error: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_error +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +*_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o +*_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA +*_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk +*_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY +*_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 +*_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY +*_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU +*_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ +*_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y +*_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M +*_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw +*_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 +*_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw +*_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M +*_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +*_llgo_int32: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int32 +*_llgo_int64: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int64 +*_llgo_io.Reader: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_io.Reader +*_llgo_io.Writer: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_io.Writer +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +*_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint8 +[]_llgo_uint8: + *[]_llgo_uint8 + _llgo_uint8 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32: + github.com/goplus/llgo/runtime/internal/runtime.memequal32 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8: + github.com/goplus/llgo/runtime/internal/runtime.memequal8 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_bool: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 + *_llgo_bool +_llgo_bytes.Buffer: + *_llgo_bytes.Buffer + bytes.struct$8M6lRFZ7Fk2XCr2laNI9Y7uQtk2A8VDBrezMuq2Fkuo$fields +_llgo_bytes.readOp: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 + *_llgo_bytes.readOp +_llgo_error: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + *_llgo_error + _llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + *_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w$out +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w$out: + _llgo_error +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + *_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out: + _llgo_int +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + *_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$in + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$out +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$in: + []_llgo_uint8 +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$out: + _llgo_int + _llgo_error +_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: + *_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o + _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$in + _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$out +_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$in: + _llgo_uint8 +_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$out: + _llgo_string + _llgo_error +_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: + *_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA + _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA$in +_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA$in: + _llgo_int +_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: + *_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk$out +_llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk$out: + _llgo_bool +_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY: + *_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY + _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY$out +_llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY$out: + []_llgo_uint8 +_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0: + *_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 + _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0$in + _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0$out +_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0$in: + _llgo_uint8 +_llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0$out: + []_llgo_uint8 + _llgo_error +_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY: + *_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY + _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY$in + _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY$out +_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY$in: + _llgo_int +_llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY$out: + []_llgo_uint8 +_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU: + *_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU + _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU$in + _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU$out +_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU$in: + _llgo_int +_llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU$out: + _llgo_int +_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: + *_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ + _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ$out +_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ$out: + _llgo_uint8 + _llgo_error +_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: + *_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y + _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y$out +_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y$out: + _llgo_int32 + _llgo_int + _llgo_error +_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: + *_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M + _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$in + _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$out +_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$in: + _llgo_int +_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$out: + _llgo_int + _llgo_bool +_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: + *_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw + _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$in + _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$out +_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$in: + _llgo_string +_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$out: + _llgo_int + _llgo_error +_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: + *_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 + _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$in + _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$out +_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$in: + _llgo_io.Reader +_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$out: + _llgo_int64 + _llgo_error +_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: + *_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw + _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$in + _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$out +_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$in: + _llgo_int32 +_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$out: + _llgo_int + _llgo_error +_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: + *_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M + _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$in + _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$out +_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$in: + _llgo_io.Writer +_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$out: + _llgo_int64 + _llgo_error +_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: + *_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg + _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg$in + _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg$out +_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg$in: + _llgo_uint8 +_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg$out: + _llgo_error +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: + _llgo_string +_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_iface$kr1iSWwMezh0B9LdQN0MhEZUNZvBlHPhlst95jAyxE0$imethods: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + *_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 + *_llgo_int +_llgo_int32: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32 + *_llgo_int32 +_llgo_int64: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 + *_llgo_int64 +_llgo_io.Reader: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + *_llgo_io.Reader + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods +_llgo_io.Writer: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + *_llgo_io.Writer + _llgo_iface$kr1iSWwMezh0B9LdQN0MhEZUNZvBlHPhlst95jAyxE0$imethods +_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal + *_llgo_string +_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 + *_llgo_uint8 +bytes.struct$8M6lRFZ7Fk2XCr2laNI9Y7uQtk2A8VDBrezMuq2Fkuo$fields: + []_llgo_uint8 + _llgo_int + _llgo_bytes.readOp +github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.init: + github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.init$guard + bytes.init + io.init +github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: + bytes.NewBuffer + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + *_llgo_bytes.Buffer + github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + [UseIface] github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: *_llgo_bytes.Buffer diff --git a/cl/_testmeta/reflect_dynamic/meta-expect.txt b/cl/_testmeta/reflect_dynamic/meta-expect.txt index 70aadcb457..5de18338cb 100644 --- a/cl/_testmeta/reflect_dynamic/meta-expect.txt +++ b/cl/_testmeta/reflect_dynamic/meta-expect.txt @@ -4,6 +4,35 @@ *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 + *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.init: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.init$guard + reflect.init +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.main: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use +github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: + github.com/goplus/llgo/runtime/internal/runtime.AllocU + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T + reflect.ValueOf + reflect.Value.MethodByName + [UseIface] github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index 3071415cbc..73912323fb 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -44,6 +44,35 @@ _llgo_reflect.Type: reflect.common _llgo_func$w6XuV-1SmW103DbauPseXBpW50HpxXAEsUsGFibl0Uw reflect.uncommon _llgo_func$iG49bujiXjI2lVflYdE0hPXlCAABL-XKRANSNJEKOio +[OrdinaryEdges] +*_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: + *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 + *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T +github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M: + github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M +github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m: + github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m +github.com/goplus/llgo/cl/_testmeta/reflect_named.init: + github.com/goplus/llgo/cl/_testmeta/reflect_named.init$guard + reflect.init +github.com/goplus/llgo/cl/_testmeta/reflect_named.main: + github.com/goplus/llgo/runtime/internal/runtime.AllocU + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + reflect.TypeOf + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + [UseIface] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T diff --git a/cl/_testmeta/typechildren_basic/meta-expect.txt b/cl/_testmeta/typechildren_basic/meta-expect.txt index dceb1b2067..dc8229a7eb 100644 --- a/cl/_testmeta/typechildren_basic/meta-expect.txt +++ b/cl/_testmeta/typechildren_basic/meta-expect.txt @@ -13,6 +13,51 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner _llgo_int +[OrdinaryEdges] +*_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner +*_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner: + github.com/goplus/llgo/runtime/internal/runtime.structequal + *_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner + _llgo_struct$MTAKiWNMPODH0G9-y5PI3BUGLd6hRciSbX1QTpCDOZg$fields +_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: + github.com/goplus/llgo/runtime/internal/runtime.structequal + *_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer + _llgo_struct$VaHvQdd7oPMFznC6BSA9M_OXdmuO77w_Ys1GnWZpjRM$fields +_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 + *_llgo_int +_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal + *_llgo_string +_llgo_struct$MTAKiWNMPODH0G9-y5PI3BUGLd6hRciSbX1QTpCDOZg$fields: + _llgo_string +_llgo_struct$VaHvQdd7oPMFznC6BSA9M_OXdmuO77w_Ys1GnWZpjRM$fields: + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner + _llgo_int +github.com/goplus/llgo/cl/_testmeta/typechildren_basic.init: + github.com/goplus/llgo/cl/_testmeta/typechildren_basic.init$guard +github.com/goplus/llgo/cl/_testmeta/typechildren_basic.main: + github.com/goplus/llgo/runtime/internal/runtime.AllocU + _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer + github.com/goplus/llgo/cl/_testmeta/typechildren_basic.use + [UseIface] github.com/goplus/llgo/cl/_testmeta/typechildren_basic.main: _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer diff --git a/internal/build/build.go b/internal/build/build.go index 047bc19504..c765511e76 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1277,6 +1277,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { aPkg.LPkg = ret if metaBuilder != nil { + extractOrdinaryEdges(metaBuilder, ret.Module()) aPkg.Meta = metaBuilder.Build() } if hook := ctx.buildConf.ModuleHook; hook != nil { diff --git a/internal/build/metadata_edges.go b/internal/build/metadata_edges.go new file mode 100644 index 0000000000..d67d67b0cc --- /dev/null +++ b/internal/build/metadata_edges.go @@ -0,0 +1,133 @@ +package build + +import ( + "strings" + + "github.com/goplus/llgo/internal/metadata" + "github.com/xgo-dev/llvm" +) + +func extractOrdinaryEdges(builder *metadata.Builder, mod llvm.Module) { + if builder == nil { + return + } + for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { + src := fn.Name() + if src == "" || fn.IsDeclaration() { + continue + } + collector := ordinaryEdgeCollector{builder: builder, src: src} + for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) { + for instr := bb.FirstInstruction(); !instr.IsNil(); instr = llvm.NextInstruction(instr) { + collector.scanOperands(instr) + } + } + } + for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) { + src := global.Name() + if src == "" { + continue + } + init := global.Initializer() + if init.IsNil() { + continue + } + collector := ordinaryEdgeCollector{builder: builder, src: src} + collector.scanGlobalInitializer(init) + } +} + +type ordinaryEdgeCollector struct { + builder *metadata.Builder + src string + seen map[llvm.Value]struct{} +} + +func (c *ordinaryEdgeCollector) scanGlobalInitializer(v llvm.Value) { + if isUncommonTypeInitializer(v) { + for i, n := 0, v.OperandsCount(); i < n; i++ { + if i == 2 { + continue + } + c.scan(v.Operand(i)) + } + return + } + c.scan(v) +} + +func (c *ordinaryEdgeCollector) scanOperands(v llvm.Value) { + for i, n := 0, v.OperandsCount(); i < n; i++ { + c.scan(v.Operand(i)) + } +} + +func (c *ordinaryEdgeCollector) scan(v llvm.Value) { + if v.IsNil() { + return + } + if isMethodTable(v) { + return + } + if name := namedModuleSymbol(v); name != "" { + c.add(name) + return + } + if v.IsAConstant().IsNil() { + return + } + if c.seen == nil { + c.seen = make(map[llvm.Value]struct{}) + } + if _, ok := c.seen[v]; ok { + return + } + c.seen[v] = struct{}{} + for i, n := 0, v.OperandsCount(); i < n; i++ { + c.scan(v.Operand(i)) + } +} + +func (c *ordinaryEdgeCollector) add(dst string) { + if dst == "" || dst == c.src { + return + } + c.builder.AddEdge(c.builder.Symbol(c.src), c.builder.Symbol(dst)) +} + +func namedModuleSymbol(v llvm.Value) string { + if !v.IsAFunction().IsNil() || !v.IsAGlobalVariable().IsNil() { + return v.Name() + } + return "" +} + +func isUncommonTypeInitializer(v llvm.Value) bool { + if v.IsAConstantStruct().IsNil() || v.OperandsCount() != 3 { + return false + } + return isMethodTable(v.Operand(2)) +} + +func isMethodTable(v llvm.Value) bool { + if v.IsNil() || v.IsAConstantArray().IsNil() || v.OperandsCount() == 0 { + return false + } + methodTy := v.Type().ElementType() + if methodTy.TypeKind() != llvm.StructTypeKind { + return false + } + if strings.HasSuffix(methodTy.StructName(), ".Method") { + return true + } + for i, n := 0, v.OperandsCount(); i < n; i++ { + method := v.Operand(i) + if method.IsAConstantStruct().IsNil() || method.OperandsCount() != 4 { + return false + } + if namedModuleSymbol(method.Operand(2)) == "" || namedModuleSymbol(method.Operand(3)) == "" { + return false + } + } + return true +} diff --git a/internal/build/metadata_edges_test.go b/internal/build/metadata_edges_test.go new file mode 100644 index 0000000000..3cc5f8d80f --- /dev/null +++ b/internal/build/metadata_edges_test.go @@ -0,0 +1,106 @@ +package build + +import ( + "testing" + + "github.com/goplus/llgo/internal/metadata" + "github.com/xgo-dev/llvm" +) + +func TestExtractOrdinaryEdgesFromFunctionAndGlobal(t *testing.T) { + ctx := llvm.NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("ordinary") + defer mod.Dispose() + + voidTy := ctx.VoidType() + fnTy := llvm.FunctionType(voidTy, nil, false) + mainFn := llvm.AddFunction(mod, "pkg.main", fnTy) + helperFn := llvm.AddFunction(mod, "pkg.helper", fnTy) + + b := ctx.NewBuilder() + defer b.Dispose() + entry := ctx.AddBasicBlock(mainFn, "entry") + b.SetInsertPointAtEnd(entry) + b.CreateCall(fnTy, helperFn, nil, "") + b.CreateRetVoid() + + global := llvm.AddGlobal(mod, llvm.PointerType(fnTy, 0), "pkg.global") + global.SetInitializer(helperFn) + + mb := metadata.NewBuilder() + extractOrdinaryEdges(mb, mod) + pm := mb.Build() + + mainSym := mb.Symbol("pkg.main") + helperSym := mb.Symbol("pkg.helper") + globalSym := mb.Symbol("pkg.global") + + if !hasOrdinaryEdge(pm, mainSym, helperSym) { + t.Fatalf("missing ordinary edge pkg.main -> pkg.helper") + } + if !hasOrdinaryEdge(pm, globalSym, helperSym) { + t.Fatalf("missing ordinary edge pkg.global -> pkg.helper") + } +} + +func TestExtractOrdinaryEdgesSkipsUncommonMethodTable(t *testing.T) { + ctx := llvm.NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("ordinary") + defer mod.Dispose() + + voidTy := ctx.VoidType() + fnTy := llvm.FunctionType(voidTy, nil, false) + ifn := llvm.AddFunction(mod, "pkg.(*T).M", fnTy) + tfn := llvm.AddFunction(mod, "pkg.T.M", fnTy) + + i8ptrTy := llvm.PointerType(ctx.Int8Type(), 0) + methodTy := ctx.StructCreateNamed("runtime/internal/runtime.Method") + methodTy.StructSetBody([]llvm.Type{i8ptrTy, i8ptrTy, llvm.PointerType(fnTy, 0), llvm.PointerType(fnTy, 0)}, false) + methods := llvm.ConstArray(methodTy, []llvm.Value{ + llvm.ConstNamedStruct(methodTy, []llvm.Value{ + llvm.ConstNull(i8ptrTy), + llvm.ConstNull(i8ptrTy), + ifn, + tfn, + }), + }) + + typeTy := ctx.StructCreateNamed("pkg.T.type") + typeTy.StructSetBody([]llvm.Type{i8ptrTy, i8ptrTy, methods.Type()}, false) + typeDesc := llvm.AddGlobal(mod, typeTy, "_llgo_pkg.T") + typeDesc.SetInitializer(llvm.ConstNamedStruct(typeTy, []llvm.Value{ + llvm.ConstNull(i8ptrTy), + llvm.ConstNull(i8ptrTy), + methods, + })) + + mb := metadata.NewBuilder() + extractOrdinaryEdges(mb, mod) + pm := mb.Build() + + typeSym := mb.Symbol("_llgo_pkg.T") + if hasOrdinaryEdge(pm, typeSym, mb.Symbol("pkg.(*T).M")) { + t.Fatalf("method table IFn was recorded as an ordinary edge") + } + if hasOrdinaryEdge(pm, typeSym, mb.Symbol("pkg.T.M")) { + t.Fatalf("method table TFn was recorded as an ordinary edge") + } +} + +func hasOrdinaryEdge(pm *metadata.PackageMeta, src, dst metadata.Symbol) bool { + found := false + pm.ForEachOrdinaryEdge(func(edgeSrc metadata.Symbol, dsts []metadata.Symbol) { + if edgeSrc != src { + return + } + for _, edgeDst := range dsts { + if edgeDst == dst { + found = true + return + } + } + }) + return found +} From 23cfb43d330ed44f986491bf3b4efdc221773d69 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 18:06:21 +0800 Subject: [PATCH 06/37] wip: add metadata cache debug tooling --- chore/metadump/main.go | 68 +++++++++++++++++++++++++++++++++++++++ internal/build/build.go | 12 ++++--- internal/build/collect.go | 3 ++ 3 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 chore/metadump/main.go diff --git a/chore/metadump/main.go b/chore/metadump/main.go new file mode 100644 index 0000000000..c41e6c10e0 --- /dev/null +++ b/chore/metadump/main.go @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 The XGo Authors (xgo.dev). 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 main + +import ( + "flag" + "fmt" + "os" + + "github.com/goplus/llgo/internal/metadata" +) + +func main() { + var out string + flag.StringVar(&out, "o", "", "write decoded metadata to file") + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: metadump [-o output] file.meta\n") + flag.PrintDefaults() + } + flag.Parse() + + if flag.NArg() != 1 { + flag.Usage() + os.Exit(2) + } + + if err := run(flag.Arg(0), out); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run(input, output string) error { + f, err := os.Open(input) + if err != nil { + return fmt.Errorf("open %s: %w", input, err) + } + defer f.Close() + + pm, err := metadata.ReadMeta(f) + if err != nil { + return fmt.Errorf("read %s: %w", input, err) + } + text := metadata.MetaString(pm) + + if output == "" { + fmt.Print(text) + return nil + } + if err := os.WriteFile(output, []byte(text), 0o644); err != nil { + return fmt.Errorf("write %s: %w", output, err) + } + return nil +} diff --git a/internal/build/build.go b/internal/build/build.go index c765511e76..6286d62aae 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -34,6 +34,7 @@ import ( "strings" "sync" "sync/atomic" + "time" "golang.org/x/tools/go/ssa" @@ -687,7 +688,7 @@ func buildAllPkgs(ctx *context, pkgs []*aPackage, verbose bool) ([]*aPackage, er ctx.tryLoadFromCache(aPkg) if verbose { if aPkg.CacheHit { - fmt.Fprintf(os.Stderr, "CACHE HIT: %s\n", pkg.PkgPath) + fmt.Fprintf(os.Stderr, "CACHE HIT: %s (meta read: %s)\n", pkg.PkgPath, aPkg.MetaReadDuration) } else { fmt.Fprintf(os.Stderr, "CACHE MISS: %s\n", pkg.PkgPath) } @@ -719,7 +720,7 @@ func buildAllPkgs(ctx *context, pkgs []*aPackage, verbose bool) ([]*aPackage, er ctx.tryLoadFromCache(aPkg) if verbose { if aPkg.CacheHit { - fmt.Fprintf(os.Stderr, "CACHE HIT: %s\n", pkg.PkgPath) + fmt.Fprintf(os.Stderr, "CACHE HIT: %s (meta read: %s)\n", pkg.PkgPath, aPkg.MetaReadDuration) } else { fmt.Fprintf(os.Stderr, "CACHE MISS: %s\n", pkg.PkgPath) } @@ -1567,9 +1568,10 @@ type aPackage struct { rewriteVars map[string]string // Cache related fields - Fingerprint string // fingerprint digest - Manifest string // manifest text content - CacheHit bool // whether cache was hit + Fingerprint string // fingerprint digest + Manifest string // manifest text content + CacheHit bool // whether cache was hit + MetaReadDuration time.Duration } type Package = *aPackage diff --git a/internal/build/collect.go b/internal/build/collect.go index 3a991d8b58..d5ce06b7bb 100644 --- a/internal/build/collect.go +++ b/internal/build/collect.go @@ -25,6 +25,7 @@ import ( "runtime" "sort" "strings" + "time" "github.com/goplus/llgo/internal/env" "github.com/goplus/llgo/internal/metadata" @@ -340,10 +341,12 @@ func (c *context) tryLoadFromCache(pkg *aPackage) bool { if err != nil { return false } + metaStart := time.Now() pkgMeta, err := readMeta(paths.Meta) if err != nil { return false } + pkg.MetaReadDuration = time.Since(metaStart) // Parse metadata from manifest [Package] section (INI format) meta, err := parseManifestMetadata(content) From fde35803ed1c1447aa840ae1742bdbca6d1d7819 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 19:31:51 +0800 Subject: [PATCH 07/37] wip: add metadata-driven deadcode overrides --- internal/build/build.go | 71 ++++++ internal/build/dce_override_test.go | 109 ++++++++ internal/dcepass/dcepass.go | 286 +++++++++++++++++++++ internal/dcepass/dcepass_test.go | 72 ++++++ internal/deadcode/analyze.go | 253 +++++++++++++++++++ internal/deadcode/analyze_test.go | 372 ++++++++++++++++++++++++++++ 6 files changed, 1163 insertions(+) create mode 100644 internal/build/dce_override_test.go create mode 100644 internal/dcepass/dcepass.go create mode 100644 internal/dcepass/dcepass_test.go create mode 100644 internal/deadcode/analyze.go create mode 100644 internal/deadcode/analyze_test.go diff --git a/internal/build/build.go b/internal/build/build.go index 6286d62aae..ae2fa2c0ed 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -42,6 +42,8 @@ import ( "github.com/goplus/llgo/internal/cabi" "github.com/goplus/llgo/internal/clang" "github.com/goplus/llgo/internal/crosscompile" + "github.com/goplus/llgo/internal/dcepass" + "github.com/goplus/llgo/internal/deadcode" "github.com/goplus/llgo/internal/env" "github.com/goplus/llgo/internal/firmware" "github.com/goplus/llgo/internal/flash" @@ -144,6 +146,7 @@ type Config struct { Verbose bool PrintCommands bool GenLL bool // generate pkg .ll files + DCE bool // enable experimental Go-like link-time DCE CheckLLFiles bool // check .ll files valid CheckLinkArgs bool // check linkargs valid ForceEspClang bool // force to use esp-clang @@ -190,6 +193,7 @@ func NewDefaultConf(mode Mode) *Config { Mode: mode, BuildMode: BuildModeExe, AbiMode: cabi.ModeAllFunc, + DCE: true, } return conf } @@ -227,6 +231,9 @@ func Do(args []string, conf *Config) ([]Package, error) { if conf.BuildMode == "" { conf.BuildMode = BuildModeExe } + if conf.BuildMode != BuildModeExe { + conf.DCE = false + } if conf.SizeReport && conf.SizeFormat == "" { conf.SizeFormat = "text" } @@ -1026,6 +1033,11 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa methodByName: methodByName, abiSymbols: linkedModuleGlobals(linkedOrder), }) + if ctx.buildConf.DCE { + if err := applyDCEOverrides(ctx, pkg, linkedOrder, entryPkg, verbose); err != nil { + return err + } + } entryObjFile, err := exportObject(ctx, "entry_main", entryPkg.ExportFile, entryPkg.LPkg) if err != nil { return err @@ -1081,6 +1093,65 @@ func linkedModuleGlobals(pkgs []Package) map[string]none { return seen } +func applyDCEOverrides(ctx *context, mainPkg *packages.Package, pkgs []Package, entryPkg Package, verbose bool) error { + metas := linkedPackageMetas(pkgs) + if len(metas) == 0 { + return nil + } + summary, err := metadata.NewGlobalSummary(metas) + if err != nil { + return err + } + roots := dceEntryRootCandidates(mainPkg) + liveSlots := deadcode.Analyze(summary, roots) + if len(liveSlots) == 0 { + return nil + } + if verbose { + liveCount := 0 + for _, slots := range liveSlots { + liveCount += len(slots) + } + fmt.Fprintf(os.Stderr, "[dce] roots=%s live method slots=%d types=%d\n", strings.Join(roots, ","), liveCount, len(liveSlots)) + } + return dcepass.EmitStrongTypeOverrides(entryPkg.LPkg.Module(), dceSourceModules(pkgs), liveSlots) +} + +func linkedPackageMetas(pkgs []Package) []*metadata.PackageMeta { + metas := make([]*metadata.PackageMeta, 0, len(pkgs)) + for _, pkg := range pkgs { + if pkg == nil || pkg.Meta == nil { + continue + } + metas = append(metas, pkg.Meta) + } + return metas +} + +func dceSourceModules(pkgs []Package) []gllvm.Module { + mods := make([]gllvm.Module, 0, len(pkgs)) + seen := make(map[gllvm.Module]bool) + for _, pkg := range pkgs { + if pkg == nil || pkg.LPkg == nil { + continue + } + mod := pkg.LPkg.Module() + if mod.IsNil() || seen[mod] { + continue + } + seen[mod] = true + mods = append(mods, mod) + } + return mods +} + +func dceEntryRootCandidates(pkg *packages.Package) []string { + if pkg == nil || pkg.PkgPath == "" { + return nil + } + return []string{pkg.PkgPath + ".init", pkg.PkgPath + ".main"} +} + // isRuntimePkg reports whether the package path belongs to the llgo runtime tree. func isRuntimePkg(pkgPath string) bool { rtRoot := env.LLGoRuntimePkg diff --git a/internal/build/dce_override_test.go b/internal/build/dce_override_test.go new file mode 100644 index 0000000000..9a4247b0a7 --- /dev/null +++ b/internal/build/dce_override_test.go @@ -0,0 +1,109 @@ +//go:build !llgo +// +build !llgo + +package build + +import ( + "strings" + "testing" + + "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/packages" + llssa "github.com/goplus/llgo/ssa" + "github.com/xgo-dev/llvm" +) + +func TestApplyDCEOverridesWritesStrongTypeOverride(t *testing.T) { + llssa.Initialize(llssa.InitAll) + ctx := &context{ + prog: llssa.NewProgram(nil), + buildConf: &Config{ + BuildMode: BuildModeExe, + Goos: "linux", + Goarch: "amd64", + DCE: true, + }, + } + + srcPkg := ctx.prog.NewPackage("pkg", "pkg") + srcMod := srcPkg.Module() + addMethodTypeGlobal(t, srcMod, "_llgo_pkg.T") + + pkgMeta := buildDCEMeta() + srcAPkg := &aPackage{ + Package: &packages.Package{PkgPath: "pkg"}, + LPkg: srcPkg, + Meta: pkgMeta, + } + entryPkg := genMainModule(ctx, llssa.PkgRuntime, &packages.Package{ + PkgPath: "pkg", + ExportFile: "pkg.a", + }, &genConfig{}) + + if err := applyDCEOverrides(ctx, &packages.Package{PkgPath: "pkg"}, []Package{srcAPkg}, entryPkg, false); err != nil { + t.Fatalf("applyDCEOverrides: %v", err) + } + + out := entryPkg.LPkg.Module().String() + if !strings.Contains(out, `@_llgo_pkg.T = constant`) { + t.Fatalf("entry module missing strong type override:\n%s", out) + } + if !strings.Contains(out, `ptr @"pkg.(*T).M", ptr @pkg.T.M`) { + t.Fatalf("live method slot was not preserved:\n%s", out) + } + if strings.Contains(out, `ptr @"pkg.(*T).N"`) || strings.Contains(out, `ptr @pkg.T.N`) { + t.Fatalf("dead method slot still references N functions:\n%s", out) + } +} + +func buildDCEMeta() *metadata.PackageMeta { + b := metadata.NewBuilder() + main := b.Symbol("pkg.main") + use := b.Symbol("pkg.use") + typ := b.Symbol("_llgo_pkg.T") + iface := b.Symbol("_llgo_iface$I") + mSig := metadata.MethodSig{Name: b.Name("M"), MType: b.Symbol("_llgo_func$X")} + nSig := metadata.MethodSig{Name: b.Name("N"), MType: b.Symbol("_llgo_func$X")} + + b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) + b.AddMethodInfo(typ, []metadata.MethodSlot{ + {Sig: mSig, IFn: b.Symbol("pkg.(*T).M"), TFn: b.Symbol("pkg.T.M")}, + {Sig: nSig, IFn: b.Symbol("pkg.(*T).N"), TFn: b.Symbol("pkg.T.N")}, + }) + b.AddEdge(main, use) + b.AddEdge(main, typ) + b.AddUseIface(main, []metadata.Symbol{typ}) + b.AddUseIfaceMethod(use, []metadata.IfaceMethodDemand{{Target: iface, Sig: mSig}}) + return b.Build() +} + +func addMethodTypeGlobal(t *testing.T, mod llvm.Module, name string) { + t.Helper() + ctx := mod.Context() + voidTy := ctx.VoidType() + fnTy := llvm.FunctionType(voidTy, nil, false) + ptrTy := llvm.PointerType(fnTy, 0) + stringTy := ctx.StructCreateNamed("runtime/internal/runtime.String") + stringTy.StructSetBody([]llvm.Type{llvm.PointerType(ctx.Int8Type(), 0), ctx.Int64Type()}, false) + methodTy := ctx.StructCreateNamed("github.com/goplus/llgo/runtime/abi.Method") + methodTy.StructSetBody([]llvm.Type{stringTy, ptrTy, ptrTy, ptrTy}, false) + + mtyp := llvm.AddGlobal(mod, ptrTy, "mtyp") + ifnM := llvm.AddFunction(mod, "pkg.(*T).M", fnTy) + tfnM := llvm.AddFunction(mod, "pkg.T.M", fnTy) + ifnN := llvm.AddFunction(mod, "pkg.(*T).N", fnTy) + tfnN := llvm.AddFunction(mod, "pkg.T.N", fnTy) + methods := llvm.ConstArray(methodTy, []llvm.Value{ + llvm.ConstNamedStruct(methodTy, []llvm.Value{llvm.ConstNull(stringTy), mtyp, ifnM, tfnM}), + llvm.ConstNamedStruct(methodTy, []llvm.Value{llvm.ConstNull(stringTy), mtyp, ifnN, tfnN}), + }) + typeTy := ctx.StructCreateNamed("pkg.T.type") + typeTy.StructSetBody([]llvm.Type{ctx.Int8Type(), methods.Type()}, false) + typeDesc := llvm.AddGlobal(mod, typeTy, name) + typeDesc.SetGlobalConstant(true) + typeDesc.SetLinkage(llvm.LinkOnceODRLinkage) + typeDesc.SetInitializer(llvm.ConstNamedStruct(typeTy, []llvm.Value{ + llvm.ConstNull(ctx.Int8Type()), + methods, + })) +} diff --git a/internal/dcepass/dcepass.go b/internal/dcepass/dcepass.go new file mode 100644 index 0000000000..ca0ef3ee38 --- /dev/null +++ b/internal/dcepass/dcepass.go @@ -0,0 +1,286 @@ +// Package dcepass rewrites ABI metadata so link-time dead code elimination can +// drop method bodies that are no longer referenced by live method slots. +package dcepass + +import ( + "fmt" + "strings" + + "github.com/xgo-dev/llvm" +) + +// EmitStrongTypeOverrides emits method-pruned strong ABI type symbols into dst. +// +// srcMods contain the original package modules. For each constant ABI type +// global with a method array, this function creates a same-name strong global in +// dst and clears IFn/TFn for method slots not listed in liveSlots[typeName]. +func EmitStrongTypeOverrides(dst llvm.Module, srcMods []llvm.Module, liveSlots map[string][]int) error { + if dst.IsNil() { + return fmt.Errorf("destination module is nil") + } + emitted := make(map[string]bool) + emitter := newOverrideEmitter(dst) + for _, src := range srcMods { + if src.IsNil() { + continue + } + for g := src.FirstGlobal(); !g.IsNil(); g = llvm.NextGlobal(g) { + name := g.Name() + if name == "" || emitted[name] || !g.IsGlobalConstant() { + continue + } + init := g.Initializer() + if init.IsNil() { + continue + } + methodsVal, elemTy, ok := methodArray(init) + if !ok || methodsVal.OperandsCount() == 0 { + continue + } + if err := emitter.emitTypeOverride(g, methodsVal, elemTy, liveSlotSet(liveSlots[name])); err != nil { + return fmt.Errorf("emit override %q: %w", name, err) + } + emitted[name] = true + } + } + return nil +} + +type overrideEmitter struct { + dst llvm.Module + values map[llvm.Value]llvm.Value +} + +func newOverrideEmitter(dst llvm.Module) *overrideEmitter { + return &overrideEmitter{ + dst: dst, + values: make(map[llvm.Value]llvm.Value), + } +} + +func (e *overrideEmitter) emitTypeOverride(srcType, methodsVal llvm.Value, elemTy llvm.Type, keepIdx map[int]bool) error { + init := srcType.Initializer() + dstType, err := e.ensureOverrideGlobal(srcType) + if err != nil { + return err + } + e.values[srcType] = dstType + + fieldCount := init.OperandsCount() + fields := make([]llvm.Value, fieldCount) + for i := 0; i < fieldCount-1; i++ { + clone, err := e.cloneConst(init.Operand(i)) + if err != nil { + return err + } + fields[i] = clone + } + + elemFields := elemTy.StructElementTypes() + if len(elemFields) < 4 { + return fmt.Errorf("method element type has %d fields", len(elemFields)) + } + zeroPtr := llvm.ConstPointerNull(elemFields[2]) + methodCount := methodsVal.OperandsCount() + methods := make([]llvm.Value, methodCount) + for i := 0; i < methodCount; i++ { + orig := methodsVal.Operand(i) + if keepIdx[i] { + clone, err := e.cloneConst(orig) + if err != nil { + return err + } + methods[i] = clone + continue + } + nameField, err := e.cloneConst(orig.Operand(0)) + if err != nil { + return err + } + mtypField, err := e.cloneConst(orig.Operand(1)) + if err != nil { + return err + } + methods[i] = llvm.ConstNamedStruct(elemTy, []llvm.Value{nameField, mtypField, zeroPtr, zeroPtr}) + } + fields[fieldCount-1] = llvm.ConstArray(elemTy, methods) + + dstType.SetInitializer(constStructOfType(init.Type(), fields)) + dstType.SetGlobalConstant(true) + dstType.SetLinkage(llvm.ExternalLinkage) + copyGlobalAttrs(dstType, srcType) + return nil +} + +func (e *overrideEmitter) ensureOverrideGlobal(src llvm.Value) (llvm.Value, error) { + name := src.Name() + if name == "" { + return llvm.Value{}, fmt.Errorf("type global has empty name") + } + dst := e.dst.NamedGlobal(name) + if dst.IsNil() { + dst = llvm.AddGlobal(e.dst, src.GlobalValueType(), name) + } + e.values[src] = dst + return dst, nil +} + +func (e *overrideEmitter) cloneConst(v llvm.Value) (llvm.Value, error) { + if v.IsNil() { + return llvm.Value{}, nil + } + if mapped, ok := e.values[v]; ok { + return mapped, nil + } + if gv := v.IsAGlobalValue(); !gv.IsNil() { + clone, err := e.cloneGlobalValue(gv) + if err != nil { + return llvm.Value{}, err + } + e.values[v] = clone + return clone, nil + } + if !v.IsAConstantStruct().IsNil() { + ops, err := e.cloneOperands(v) + if err != nil { + return llvm.Value{}, err + } + clone := constStructOfType(v.Type(), ops) + e.values[v] = clone + return clone, nil + } + return v, nil +} + +func (e *overrideEmitter) cloneOperands(v llvm.Value) ([]llvm.Value, error) { + n := v.OperandsCount() + ops := make([]llvm.Value, n) + for i := 0; i < n; i++ { + clone, err := e.cloneConst(v.Operand(i)) + if err != nil { + return nil, err + } + ops[i] = clone + } + return ops, nil +} + +func (e *overrideEmitter) cloneGlobalValue(v llvm.Value) (llvm.Value, error) { + if mapped, ok := e.values[v]; ok { + return mapped, nil + } + if fn := v.IsAFunction(); !fn.IsNil() { + name := fn.Name() + if name == "" { + return llvm.Value{}, fmt.Errorf("function ref has empty name") + } + dstFn := e.dst.NamedFunction(name) + if dstFn.IsNil() { + dstFn = llvm.AddFunction(e.dst, name, fn.GlobalValueType()) + } + e.values[v] = dstFn + return dstFn, nil + } + if gv := v.IsAGlobalVariable(); !gv.IsNil() { + clone, err := e.cloneGlobalVariable(gv) + if err != nil { + return llvm.Value{}, err + } + e.values[v] = clone + return clone, nil + } + name := v.Name() + if name == "" { + return llvm.Value{}, fmt.Errorf("unsupported unnamed global ref") + } + dstG := e.dst.NamedGlobal(name) + if dstG.IsNil() { + dstG = llvm.AddGlobal(e.dst, v.GlobalValueType(), name) + dstG.SetLinkage(llvm.ExternalLinkage) + } + e.values[v] = dstG + return dstG, nil +} + +func (e *overrideEmitter) cloneGlobalVariable(src llvm.Value) (llvm.Value, error) { + if mapped, ok := e.values[src]; ok { + return mapped, nil + } + name := src.Name() + local := name == "" || isLocalLinkage(src.Linkage()) + if !local { + dst := e.dst.NamedGlobal(name) + if dst.IsNil() { + dst = llvm.AddGlobal(e.dst, src.GlobalValueType(), name) + dst.SetLinkage(llvm.ExternalLinkage) + } + e.values[src] = dst + return dst, nil + } + + dst := llvm.AddGlobal(e.dst, src.GlobalValueType(), "") + e.values[src] = dst + copyGlobalAttrs(dst, src) + dst.SetLinkage(src.Linkage()) + dst.SetGlobalConstant(src.IsGlobalConstant()) + if init := src.Initializer(); !init.IsNil() { + cloneInit, err := e.cloneConst(init) + if err != nil { + return llvm.Value{}, err + } + dst.SetInitializer(cloneInit) + } + return dst, nil +} + +func methodArray(init llvm.Value) (llvm.Value, llvm.Type, bool) { + fieldCount := init.OperandsCount() + if fieldCount == 0 { + return llvm.Value{}, llvm.Type{}, false + } + methodsVal := init.Operand(fieldCount - 1) + if methodsVal.Type().TypeKind() != llvm.ArrayTypeKind { + return llvm.Value{}, llvm.Type{}, false + } + elemTy := methodsVal.Type().ElementType() + if elemTy.TypeKind() != llvm.StructTypeKind { + return llvm.Value{}, llvm.Type{}, false + } + if elemTy.StructElementTypesCount() != 4 { + return llvm.Value{}, llvm.Type{}, false + } + if !strings.Contains(elemTy.StructName(), "runtime/abi.Method") { + return llvm.Value{}, llvm.Type{}, false + } + return methodsVal, elemTy, true +} + +func liveSlotSet(slots []int) map[int]bool { + out := make(map[int]bool, len(slots)) + for _, slot := range slots { + out[slot] = true + } + return out +} + +func copyGlobalAttrs(dst, src llvm.Value) { + dst.SetVisibility(src.Visibility()) + if sec := src.Section(); sec != "" { + dst.SetSection(sec) + } + dst.SetThreadLocal(src.IsThreadLocal()) + if align := src.Alignment(); align > 0 { + dst.SetAlignment(align) + } +} + +func isLocalLinkage(linkage llvm.Linkage) bool { + return linkage == llvm.PrivateLinkage || linkage == llvm.InternalLinkage +} + +func constStructOfType(typ llvm.Type, fields []llvm.Value) llvm.Value { + if typ.StructName() != "" { + return llvm.ConstNamedStruct(typ, fields) + } + return llvm.ConstStruct(fields, typ.IsStructPacked()) +} diff --git a/internal/dcepass/dcepass_test.go b/internal/dcepass/dcepass_test.go new file mode 100644 index 0000000000..c155848196 --- /dev/null +++ b/internal/dcepass/dcepass_test.go @@ -0,0 +1,72 @@ +package dcepass + +import ( + "strings" + "testing" + + "github.com/xgo-dev/llvm" +) + +func TestEmitStrongTypeOverridesPrunesDeadMethodSlots(t *testing.T) { + ctx := llvm.NewContext() + defer ctx.Dispose() + + src := ctx.NewModule("src") + defer src.Dispose() + dst := ctx.NewModule("dst") + defer dst.Dispose() + + voidTy := ctx.VoidType() + fnTy := llvm.FunctionType(voidTy, nil, false) + ptrTy := llvm.PointerType(fnTy, 0) + stringTy := ctx.StructCreateNamed("runtime/internal/runtime.String") + stringTy.StructSetBody([]llvm.Type{llvm.PointerType(ctx.Int8Type(), 0), ctx.Int64Type()}, false) + methodTy := ctx.StructCreateNamed("github.com/goplus/llgo/runtime/abi.Method") + methodTy.StructSetBody([]llvm.Type{stringTy, ptrTy, ptrTy, ptrTy}, false) + + mtyp := llvm.AddGlobal(src, ptrTy, "mtyp") + ifnM := llvm.AddFunction(src, "pkg.(*T).M", fnTy) + tfnM := llvm.AddFunction(src, "pkg.T.M", fnTy) + ifnN := llvm.AddFunction(src, "pkg.(*T).N", fnTy) + tfnN := llvm.AddFunction(src, "pkg.T.N", fnTy) + + methods := llvm.ConstArray(methodTy, []llvm.Value{ + llvm.ConstNamedStruct(methodTy, []llvm.Value{ + llvm.ConstNull(stringTy), + mtyp, + ifnM, + tfnM, + }), + llvm.ConstNamedStruct(methodTy, []llvm.Value{ + llvm.ConstNull(stringTy), + mtyp, + ifnN, + tfnN, + }), + }) + typeTy := ctx.StructCreateNamed("pkg.T.type") + typeTy.StructSetBody([]llvm.Type{ctx.Int8Type(), methods.Type()}, false) + typeDesc := llvm.AddGlobal(src, typeTy, "_llgo_pkg.T") + typeDesc.SetGlobalConstant(true) + typeDesc.SetLinkage(llvm.LinkOnceODRLinkage) + typeDesc.SetInitializer(llvm.ConstNamedStruct(typeTy, []llvm.Value{ + llvm.ConstNull(ctx.Int8Type()), + methods, + })) + + liveSlots := map[string][]int{"_llgo_pkg.T": {0}} + if err := EmitStrongTypeOverrides(dst, []llvm.Module{src}, liveSlots); err != nil { + t.Fatalf("EmitStrongTypeOverrides: %v", err) + } + + out := dst.String() + if !strings.Contains(out, `@_llgo_pkg.T = constant`) { + t.Fatalf("override type global was not emitted as a strong constant:\n%s", out) + } + if !strings.Contains(out, `ptr @"pkg.(*T).M", ptr @pkg.T.M`) { + t.Fatalf("live method slot was not preserved:\n%s", out) + } + if strings.Contains(out, `ptr @"pkg.(*T).N"`) || strings.Contains(out, `ptr @pkg.T.N`) { + t.Fatalf("dead method slot still references N functions:\n%s", out) + } +} diff --git a/internal/deadcode/analyze.go b/internal/deadcode/analyze.go new file mode 100644 index 0000000000..d8877f3e7e --- /dev/null +++ b/internal/deadcode/analyze.go @@ -0,0 +1,253 @@ +package deadcode + +import ( + "go/token" + "sort" + + "github.com/goplus/llgo/internal/metadata" +) + +type ifaceMethodKey struct { + iface metadata.Symbol + sig metadata.MethodSig +} + +type methodID struct { + owner metadata.Symbol + slot int +} + +type methodRef struct { + owner metadata.Symbol + slot int + slotInfo metadata.MethodSlot +} + +type pass struct { + info *metadata.GlobalSummary + + methodImplKeys map[methodID][]ifaceMethodKey + + reachable map[metadata.Symbol]struct{} + usedInIface map[metadata.Symbol]struct{} + processedIfaceTy map[metadata.Symbol]struct{} + workQueue []metadata.Symbol + + ifaceMethod map[ifaceMethodKey]struct{} + genericIfaceMethod map[metadata.Name]struct{} + reflectSeen bool + + markableMethods []methodRef + markedMethods map[methodID]struct{} + liveSlots map[metadata.Symbol][]int +} + +// Analyze returns live ABI method slot indexes by concrete type symbol name. +func Analyze(info *metadata.GlobalSummary, rootNames []string) map[string][]int { + roots := make([]metadata.Symbol, 0, len(rootNames)) + for _, name := range rootNames { + if sym, ok := info.LookupSymbol(name); ok { + roots = append(roots, sym) + } + } + + liveSlots := deadcode(info, roots) + out := make(map[string][]int, len(liveSlots)) + for typ, slots := range liveSlots { + name := info.SymbolName(typ) + sorted := append([]int(nil), slots...) + sort.Ints(sorted) + out[name] = sorted + } + return out +} + +func deadcode(info *metadata.GlobalSummary, roots []metadata.Symbol) map[metadata.Symbol][]int { + d := &pass{ + info: info, + methodImplKeys: make(map[methodID][]ifaceMethodKey), + reachable: make(map[metadata.Symbol]struct{}), + usedInIface: make(map[metadata.Symbol]struct{}), + processedIfaceTy: make(map[metadata.Symbol]struct{}), + ifaceMethod: make(map[ifaceMethodKey]struct{}), + genericIfaceMethod: make(map[metadata.Name]struct{}), + markedMethods: make(map[methodID]struct{}), + liveSlots: make(map[metadata.Symbol][]int), + } + d.buildMethodImplKeys() + + for _, root := range roots { + d.markReachable(root) + } + + for { + d.flood() + changed := d.methodMarkingLoop() + if len(d.workQueue) == 0 && !changed { + return d.liveSlots + } + } +} + +func (d *pass) buildMethodImplKeys() { + methodRefs := make(map[metadata.MethodSig][]metadata.Symbol) + for _, iface := range d.info.Interfaces() { + for _, sig := range d.info.InterfaceMethods(iface) { + methodRefs[sig] = appendSymbolUnique(methodRefs[sig], iface) + } + } + + for _, typ := range d.info.ConcreteTypes() { + slots := d.info.MethodSlots(typ) + impls := make(map[metadata.Symbol]int) + seen := make(map[ifaceMethodKey]struct{}) + + for _, slot := range slots { + for _, iface := range methodRefs[slot.Sig] { + key := ifaceMethodKey{iface: iface, sig: slot.Sig} + if _, ok := seen[key]; ok { + continue + } + seen[key] = struct{}{} + impls[iface]++ + } + } + + for slotIndex, slot := range slots { + id := methodID{owner: typ, slot: slotIndex} + for _, iface := range methodRefs[slot.Sig] { + if impls[iface] == len(d.info.InterfaceMethods(iface)) { + key := ifaceMethodKey{iface: iface, sig: slot.Sig} + d.methodImplKeys[id] = append(d.methodImplKeys[id], key) + } + } + } + } +} + +func (d *pass) flood() { + for len(d.workQueue) > 0 { + sym := d.popWork() + + if d.info.HasReflectMethod(sym) { + d.reflectSeen = true + } + + for _, dst := range d.info.OrdinaryEdges(sym) { + d.markReachable(dst) + } + + for _, typ := range d.info.UseIface(sym) { + d.markUsedInIface(typ) + } + + for _, demand := range d.info.UseIfaceMethod(sym) { + key := ifaceMethodKey{iface: demand.Target, sig: demand.Sig} + d.ifaceMethod[key] = struct{}{} + } + + for _, name := range d.info.UseNamedMethod(sym) { + d.genericIfaceMethod[name] = struct{}{} + } + + if _, used := d.usedInIface[sym]; used { + if _, processed := d.processedIfaceTy[sym]; !processed { + d.processedIfaceTy[sym] = struct{}{} + for slot, slotInfo := range d.info.MethodSlots(sym) { + d.markableMethods = append(d.markableMethods, methodRef{ + owner: sym, + slot: slot, + slotInfo: slotInfo, + }) + } + } + } + } +} + +func (d *pass) methodMarkingLoop() bool { + changed := false + rem := d.markableMethods[:0] + + for _, method := range d.markableMethods { + if d.shouldKeep(method) { + if d.markMethod(method) { + changed = true + } + continue + } + rem = append(rem, method) + } + + d.markableMethods = rem + return changed +} + +func (d *pass) shouldKeep(method methodRef) bool { + if d.reflectSeen && token.IsExported(d.info.Name(method.slotInfo.Sig.Name)) { + return true + } + + if _, ok := d.genericIfaceMethod[method.slotInfo.Sig.Name]; ok { + return true + } + + id := methodID{owner: method.owner, slot: method.slot} + for _, key := range d.methodImplKeys[id] { + if _, ok := d.ifaceMethod[key]; ok { + return true + } + } + return false +} + +func (d *pass) markMethod(method methodRef) bool { + id := methodID{owner: method.owner, slot: method.slot} + if _, ok := d.markedMethods[id]; ok { + return false + } + d.markedMethods[id] = struct{}{} + d.liveSlots[method.owner] = append(d.liveSlots[method.owner], method.slot) + + d.markReachable(method.slotInfo.Sig.MType) + d.markReachable(method.slotInfo.IFn) + d.markReachable(method.slotInfo.TFn) + return true +} + +func (d *pass) markReachable(sym metadata.Symbol) { + if _, ok := d.reachable[sym]; ok { + return + } + d.reachable[sym] = struct{}{} + d.workQueue = append(d.workQueue, sym) +} + +func (d *pass) markUsedInIface(typ metadata.Symbol) { + if _, ok := d.usedInIface[typ]; ok { + return + } + d.usedInIface[typ] = struct{}{} + if _, ok := d.reachable[typ]; ok { + d.workQueue = append(d.workQueue, typ) + } + for _, child := range d.info.TypeChildren(typ) { + d.markUsedInIface(child) + } +} + +func (d *pass) popWork() metadata.Symbol { + sym := d.workQueue[0] + copy(d.workQueue, d.workQueue[1:]) + d.workQueue = d.workQueue[:len(d.workQueue)-1] + return sym +} + +func appendSymbolUnique(items []metadata.Symbol, item metadata.Symbol) []metadata.Symbol { + for _, existing := range items { + if existing == item { + return items + } + } + return append(items, item) +} diff --git a/internal/deadcode/analyze_test.go b/internal/deadcode/analyze_test.go new file mode 100644 index 0000000000..20e1e59ab1 --- /dev/null +++ b/internal/deadcode/analyze_test.go @@ -0,0 +1,372 @@ +package deadcode + +import ( + "reflect" + "testing" + + "github.com/goplus/llgo/internal/metadata" +) + +func TestAnalyze(t *testing.T) { + tests := []deadcodeCase{ + // type I interface{ M() } + // type T struct{} + // func (T) M() {} + // func (T) N() {} + // func main() { use(T{}) } + // func use(i I) { i.M() } + { + name: "keeps interface method implementation", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + use := b.Symbol("pkg.use") + typ := b.Symbol("_llgo_pkg.T") + iface := b.Symbol("_llgo_iface$I") + mSig := methodSig(b, "M") + nSig := methodSig(b, "N") + + b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), + methodSlot(b, nSig, "pkg.(*T).N", "pkg.T.N"), + }) + b.AddEdge(main, use) + b.AddEdge(main, typ) + b.AddUseIface(main, []metadata.Symbol{typ}) + b.AddUseIfaceMethod(use, []metadata.IfaceMethodDemand{{ + Target: iface, + Sig: mSig, + }}) + }), + want: map[string][]int{"_llgo_pkg.T": {0}}, + }, + // type I interface{ M(); N() } + // type J interface{ M() } + // type T struct{} + // func (T) M() {} + // func main() { useJ(T{}); useI(nil) } + // func useJ(j J) {} + // func useI(i I) { i.M() } + { + name: "requires concrete type to implement whole interface", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + useJ := b.Symbol("pkg.useJ") + useI := b.Symbol("pkg.useI") + typ := b.Symbol("_llgo_pkg.T") + iface := b.Symbol("_llgo_iface$I") + compatibleIface := b.Symbol("_llgo_iface$J") + mSig := methodSig(b, "M") + nSig := methodSig(b, "N") + + b.AddIfaceEntry(iface, []metadata.MethodSig{mSig, nSig}) + b.AddIfaceEntry(compatibleIface, []metadata.MethodSig{mSig}) + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), + }) + b.AddEdge(main, useJ) + b.AddEdge(main, useI) + b.AddEdge(main, typ) + b.AddUseIface(main, []metadata.Symbol{typ}) + b.AddUseIfaceMethod(useI, []metadata.IfaceMethodDemand{{ + Target: iface, + Sig: mSig, + }}) + }), + want: map[string][]int{}, + }, + // type I interface{ M(int) } + // type T struct{} + // func (T) M(string) {} + // func main() { use(T{}) } + // func use(i I) { i.M(0) } + { + name: "requires method type to match", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + use := b.Symbol("pkg.use") + typ := b.Symbol("_llgo_pkg.T") + iface := b.Symbol("_llgo_iface$I") + ifaceMSig := methodSigWithType(b, "M", "_llgo_func$int") + typeMSig := methodSigWithType(b, "M", "_llgo_func$string") + + b.AddIfaceEntry(iface, []metadata.MethodSig{ifaceMSig}) + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, typeMSig, "pkg.(*T).M", "pkg.T.M"), + }) + b.AddEdge(main, use) + b.AddEdge(main, typ) + b.AddUseIface(main, []metadata.Symbol{typ}) + b.AddUseIfaceMethod(use, []metadata.IfaceMethodDemand{{ + Target: iface, + Sig: ifaceMSig, + }}) + }), + want: map[string][]int{}, + }, + // type I interface{ M() } + // type T struct{} + // func (T) M() {} + // func main() { useI(); makeIface() } + // func useI(i I) { i.M() } + // func makeIface() any { return T{} } + { + name: "matches demand recorded before type enters interface semantics", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + useI := b.Symbol("pkg.useI") + makeIface := b.Symbol("pkg.makeIface") + typ := b.Symbol("_llgo_pkg.T") + iface := b.Symbol("_llgo_iface$I") + mSig := methodSig(b, "M") + + b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), + }) + b.AddEdge(main, useI) + b.AddEdge(main, makeIface) + b.AddEdge(makeIface, typ) + b.AddUseIfaceMethod(useI, []metadata.IfaceMethodDemand{{ + Target: iface, + Sig: mSig, + }}) + b.AddUseIface(makeIface, []metadata.Symbol{typ}) + }), + want: map[string][]int{"_llgo_pkg.T": {0}}, + }, + // type I interface{ M() } + // type Child struct{} + // func (Child) M() {} + // type T struct{ Child } + // func (T) M() {} + // func main() { use(T{}) } + // func use(i I) { i.M() } + { + name: "propagates interface use through type children", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + use := b.Symbol("pkg.use") + typ := b.Symbol("_llgo_pkg.T") + child := b.Symbol("_llgo_pkg.Child") + iface := b.Symbol("_llgo_iface$I") + mSig := methodSig(b, "M") + + b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), + }) + b.AddMethodInfo(child, []metadata.MethodSlot{ + methodSlot(b, mSig, "pkg.(*Child).M", "pkg.Child.M"), + }) + b.AddTypeChild(typ, child) + b.AddEdge(main, use) + b.AddEdge(main, typ) + b.AddEdge(typ, child) + b.AddUseIface(main, []metadata.Symbol{typ}) + b.AddUseIfaceMethod(use, []metadata.IfaceMethodDemand{{ + Target: iface, + Sig: mSig, + }}) + }), + want: map[string][]int{ + "_llgo_pkg.Child": {0}, + "_llgo_pkg.T": {0}, + }, + }, + // type T struct{} + // func (T) M() {} + // func (T) N() {} + // func main() { use(T{}) } + // func use(v any) { reflect.ValueOf(v).MethodByName("M") } + { + name: "constant MethodByName keeps same-name method", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + use := b.Symbol("pkg.use") + typ := b.Symbol("_llgo_pkg.T") + mSig := methodSig(b, "M") + nSig := methodSig(b, "N") + + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), + methodSlot(b, nSig, "pkg.(*T).N", "pkg.T.N"), + }) + b.AddEdge(main, use) + b.AddEdge(main, typ) + b.AddUseIface(main, []metadata.Symbol{typ}) + b.AddUseNamedMethod(use, []metadata.Name{mSig.Name}) + }), + want: map[string][]int{"_llgo_pkg.T": {0}}, + }, + // type T struct{} + // func (T) M() {} + // func (T) N() {} + // func (T) m() {} + // func main() { use(T{}) } + // func use(v any) { reflect.ValueOf(v).Method(0) } + { + name: "reflection keeps exported methods only", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + use := b.Symbol("pkg.use") + typ := b.Symbol("_llgo_pkg.T") + mSig := methodSig(b, "M") + nSig := methodSig(b, "N") + unexportedSig := methodSig(b, "m") + + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), + methodSlot(b, nSig, "pkg.(*T).N", "pkg.T.N"), + methodSlot(b, unexportedSig, "pkg.(*T).m", "pkg.T.m"), + }) + b.AddEdge(main, use) + b.AddEdge(main, typ) + b.AddUseIface(main, []metadata.Symbol{typ}) + b.AddReflectMethod(use) + }), + want: map[string][]int{"_llgo_pkg.T": {0, 1}}, + }, + // type I interface{ M() } + // type J interface{ N() } + // type T struct{} + // type U struct{} + // func (T) M() { callU() } + // func (U) N() {} + // func main() { useT(T{}) } + // func useT(i I) { i.M() } + // func callU() { useU(U{}) } + // func useU(j J) { j.N() } + { + name: "live method body can introduce new interface demands", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + useT := b.Symbol("pkg.useT") + callU := b.Symbol("pkg.callU") + useU := b.Symbol("pkg.useU") + typT := b.Symbol("_llgo_pkg.T") + typU := b.Symbol("_llgo_pkg.U") + ifaceI := b.Symbol("_llgo_iface$I") + ifaceJ := b.Symbol("_llgo_iface$J") + mSig := methodSig(b, "M") + nSig := methodSig(b, "N") + + b.AddIfaceEntry(ifaceI, []metadata.MethodSig{mSig}) + b.AddIfaceEntry(ifaceJ, []metadata.MethodSig{nSig}) + b.AddMethodInfo(typT, []metadata.MethodSlot{ + methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), + }) + b.AddMethodInfo(typU, []metadata.MethodSlot{ + methodSlot(b, nSig, "pkg.(*U).N", "pkg.U.N"), + }) + b.AddEdge(main, useT) + b.AddEdge(main, typT) + b.AddUseIface(main, []metadata.Symbol{typT}) + b.AddUseIfaceMethod(useT, []metadata.IfaceMethodDemand{{ + Target: ifaceI, + Sig: mSig, + }}) + b.AddEdge(b.Symbol("pkg.T.M"), callU) + b.AddEdge(callU, useU) + b.AddEdge(callU, typU) + b.AddUseIface(callU, []metadata.Symbol{typU}) + b.AddUseIfaceMethod(useU, []metadata.IfaceMethodDemand{{ + Target: ifaceJ, + Sig: nSig, + }}) + }), + want: map[string][]int{ + "_llgo_pkg.T": {0}, + "_llgo_pkg.U": {0}, + }, + }, + // type I interface{ M() } + // type T struct{} + // func (T) M() {} + // func main() { _ = T{} } + // func unreachable(i I) { i.M(); reflect.ValueOf(i).MethodByName("M") } + { + name: "ignores unreachable semantic facts", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + unreachable := b.Symbol("pkg.unreachable") + typ := b.Symbol("_llgo_pkg.T") + iface := b.Symbol("_llgo_iface$I") + mSig := methodSig(b, "M") + + b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), + }) + b.AddEdge(main, typ) + b.AddUseIface(unreachable, []metadata.Symbol{typ}) + b.AddUseIfaceMethod(unreachable, []metadata.IfaceMethodDemand{{ + Target: iface, + Sig: mSig, + }}) + b.AddUseNamedMethod(unreachable, []metadata.Name{mSig.Name}) + b.AddReflectMethod(unreachable) + }), + want: map[string][]int{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Analyze(newSummary(t, tt.summary), tt.roots) + if !reflect.DeepEqual(got, tt.want) { + t.Fatalf("Analyze() = %#v, want %#v", got, tt.want) + } + }) + } +} + +type deadcodeCase struct { + name string + summary *metadata.PackageMeta + roots []string + want map[string][]int +} + +func buildPackage(fn func(*metadata.Builder)) *metadata.PackageMeta { + b := metadata.NewBuilder() + fn(b) + return b.Build() +} + +func methodSig(b *metadata.Builder, name string) metadata.MethodSig { + return methodSigWithType(b, name, "_llgo_func$X") +} + +func methodSigWithType(b *metadata.Builder, name, mtype string) metadata.MethodSig { + return metadata.MethodSig{ + Name: b.Name(name), + MType: b.Symbol(mtype), + } +} + +func methodSlot(b *metadata.Builder, sig metadata.MethodSig, ifn, tfn string) metadata.MethodSlot { + return metadata.MethodSlot{ + Sig: sig, + IFn: b.Symbol(ifn), + TFn: b.Symbol(tfn), + } +} + +func newSummary(t *testing.T, pkgs ...*metadata.PackageMeta) *metadata.GlobalSummary { + t.Helper() + summary, err := metadata.NewGlobalSummary(pkgs) + if err != nil { + t.Fatalf("NewGlobalSummary: %v", err) + } + return summary +} From 8a1843f3d9556a492e7d779214b1630bdd8be17e Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 20:13:32 +0800 Subject: [PATCH 08/37] fix deadcode interface method matching --- internal/build/build.go | 1 + internal/dcepass/dcepass.go | 36 ++++++++++++++- internal/dcepass/dcepass_test.go | 52 ++++++++++++++++++++++ internal/deadcode/analyze.go | 18 ++++++-- internal/deadcode/analyze_test.go | 73 +++++++++++++++++++++++++++++++ 5 files changed, 175 insertions(+), 5 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index ae2fa2c0ed..db2321ad9b 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1113,6 +1113,7 @@ func applyDCEOverrides(ctx *context, mainPkg *packages.Package, pkgs []Package, liveCount += len(slots) } fmt.Fprintf(os.Stderr, "[dce] roots=%s live method slots=%d types=%d\n", strings.Join(roots, ","), liveCount, len(liveSlots)) + return dcepass.EmitStrongTypeOverridesDebug(entryPkg.LPkg.Module(), dceSourceModules(pkgs), liveSlots, os.Stderr) } return dcepass.EmitStrongTypeOverrides(entryPkg.LPkg.Module(), dceSourceModules(pkgs), liveSlots) } diff --git a/internal/dcepass/dcepass.go b/internal/dcepass/dcepass.go index ca0ef3ee38..7b29c9982f 100644 --- a/internal/dcepass/dcepass.go +++ b/internal/dcepass/dcepass.go @@ -4,6 +4,7 @@ package dcepass import ( "fmt" + "io" "strings" "github.com/xgo-dev/llvm" @@ -15,6 +16,13 @@ import ( // global with a method array, this function creates a same-name strong global in // dst and clears IFn/TFn for method slots not listed in liveSlots[typeName]. func EmitStrongTypeOverrides(dst llvm.Module, srcMods []llvm.Module, liveSlots map[string][]int) error { + return EmitStrongTypeOverridesDebug(dst, srcMods, liveSlots, nil) +} + +// EmitStrongTypeOverridesDebug is like EmitStrongTypeOverrides, and writes one +// debug line per method slot whose IFn/TFn references are cleared when logw is +// non-nil. +func EmitStrongTypeOverridesDebug(dst llvm.Module, srcMods []llvm.Module, liveSlots map[string][]int, logw io.Writer) error { if dst.IsNil() { return fmt.Errorf("destination module is nil") } @@ -37,7 +45,7 @@ func EmitStrongTypeOverrides(dst llvm.Module, srcMods []llvm.Module, liveSlots m if !ok || methodsVal.OperandsCount() == 0 { continue } - if err := emitter.emitTypeOverride(g, methodsVal, elemTy, liveSlotSet(liveSlots[name])); err != nil { + if err := emitter.emitTypeOverride(g, methodsVal, elemTy, liveSlotSet(liveSlots[name]), logw); err != nil { return fmt.Errorf("emit override %q: %w", name, err) } emitted[name] = true @@ -58,7 +66,7 @@ func newOverrideEmitter(dst llvm.Module) *overrideEmitter { } } -func (e *overrideEmitter) emitTypeOverride(srcType, methodsVal llvm.Value, elemTy llvm.Type, keepIdx map[int]bool) error { +func (e *overrideEmitter) emitTypeOverride(srcType, methodsVal llvm.Value, elemTy llvm.Type, keepIdx map[int]bool, logw io.Writer) error { init := srcType.Initializer() dstType, err := e.ensureOverrideGlobal(srcType) if err != nil { @@ -93,6 +101,9 @@ func (e *overrideEmitter) emitTypeOverride(srcType, methodsVal llvm.Value, elemT methods[i] = clone continue } + if logw != nil { + fmt.Fprintf(logw, "[dce] drop method %s[%d] ifn=%s tfn=%s\n", srcType.Name(), i, valueName(orig.Operand(2)), valueName(orig.Operand(3))) + } nameField, err := e.cloneConst(orig.Operand(0)) if err != nil { return err @@ -278,6 +289,27 @@ func isLocalLinkage(linkage llvm.Linkage) bool { return linkage == llvm.PrivateLinkage || linkage == llvm.InternalLinkage } +func valueName(v llvm.Value) string { + if v.IsNil() { + return "" + } + if !v.IsAGlobalValue().IsNil() { + if name := v.Name(); name != "" { + return name + } + } + if !v.IsAConstantExpr().IsNil() && v.OperandsCount() > 0 { + return valueName(v.Operand(0)) + } + if !v.IsAConstantPointerNull().IsNil() || v.IsNull() { + return "" + } + if name := v.Name(); name != "" { + return name + } + return v.String() +} + func constStructOfType(typ llvm.Type, fields []llvm.Value) llvm.Value { if typ.StructName() != "" { return llvm.ConstNamedStruct(typ, fields) diff --git a/internal/dcepass/dcepass_test.go b/internal/dcepass/dcepass_test.go index c155848196..5db149e32f 100644 --- a/internal/dcepass/dcepass_test.go +++ b/internal/dcepass/dcepass_test.go @@ -1,6 +1,7 @@ package dcepass import ( + "bytes" "strings" "testing" @@ -70,3 +71,54 @@ func TestEmitStrongTypeOverridesPrunesDeadMethodSlots(t *testing.T) { t.Fatalf("dead method slot still references N functions:\n%s", out) } } + +func TestEmitStrongTypeOverridesLogsDroppedMethodSlots(t *testing.T) { + ctx := llvm.NewContext() + defer ctx.Dispose() + + src := ctx.NewModule("src") + defer src.Dispose() + dst := ctx.NewModule("dst") + defer dst.Dispose() + + voidTy := ctx.VoidType() + fnTy := llvm.FunctionType(voidTy, nil, false) + ptrTy := llvm.PointerType(fnTy, 0) + stringTy := ctx.StructCreateNamed("runtime/internal/runtime.String") + stringTy.StructSetBody([]llvm.Type{llvm.PointerType(ctx.Int8Type(), 0), ctx.Int64Type()}, false) + methodTy := ctx.StructCreateNamed("github.com/goplus/llgo/runtime/abi.Method") + methodTy.StructSetBody([]llvm.Type{stringTy, ptrTy, ptrTy, ptrTy}, false) + + mtyp := llvm.AddGlobal(src, ptrTy, "mtyp") + ifnM := llvm.AddFunction(src, "pkg.(*T).M", fnTy) + tfnM := llvm.AddFunction(src, "pkg.T.M", fnTy) + + methods := llvm.ConstArray(methodTy, []llvm.Value{ + llvm.ConstNamedStruct(methodTy, []llvm.Value{ + llvm.ConstNull(stringTy), + mtyp, + ifnM, + tfnM, + }), + }) + typeTy := ctx.StructCreateNamed("pkg.T.type") + typeTy.StructSetBody([]llvm.Type{ctx.Int8Type(), methods.Type()}, false) + typeDesc := llvm.AddGlobal(src, typeTy, "_llgo_pkg.T") + typeDesc.SetGlobalConstant(true) + typeDesc.SetLinkage(llvm.LinkOnceODRLinkage) + typeDesc.SetInitializer(llvm.ConstNamedStruct(typeTy, []llvm.Value{ + llvm.ConstNull(ctx.Int8Type()), + methods, + })) + + var log bytes.Buffer + if err := EmitStrongTypeOverridesDebug(dst, []llvm.Module{src}, nil, &log); err != nil { + t.Fatalf("EmitStrongTypeOverridesDebug: %v", err) + } + + got := log.String() + want := `[dce] drop method _llgo_pkg.T[0] ifn=pkg.(*T).M tfn=pkg.T.M` + if !strings.Contains(got, want) { + t.Fatalf("debug log missing dropped method slot\nwant: %s\ngot:\n%s", want, got) + } +} diff --git a/internal/deadcode/analyze.go b/internal/deadcode/analyze.go index d8877f3e7e..bda2bdc2e5 100644 --- a/internal/deadcode/analyze.go +++ b/internal/deadcode/analyze.go @@ -12,6 +12,11 @@ type ifaceMethodKey struct { sig metadata.MethodSig } +type ifaceMethodName struct { + iface metadata.Symbol + name metadata.Name +} + type methodID struct { owner metadata.Symbol slot int @@ -91,20 +96,27 @@ func deadcode(info *metadata.GlobalSummary, roots []metadata.Symbol) map[metadat func (d *pass) buildMethodImplKeys() { methodRefs := make(map[metadata.MethodSig][]metadata.Symbol) + ifaceMethodCounts := make(map[metadata.Symbol]int) for _, iface := range d.info.Interfaces() { + seenNames := make(map[metadata.Name]struct{}) for _, sig := range d.info.InterfaceMethods(iface) { methodRefs[sig] = appendSymbolUnique(methodRefs[sig], iface) + if _, ok := seenNames[sig.Name]; ok { + continue + } + seenNames[sig.Name] = struct{}{} + ifaceMethodCounts[iface]++ } } for _, typ := range d.info.ConcreteTypes() { slots := d.info.MethodSlots(typ) impls := make(map[metadata.Symbol]int) - seen := make(map[ifaceMethodKey]struct{}) + seen := make(map[ifaceMethodName]struct{}) for _, slot := range slots { for _, iface := range methodRefs[slot.Sig] { - key := ifaceMethodKey{iface: iface, sig: slot.Sig} + key := ifaceMethodName{iface: iface, name: slot.Sig.Name} if _, ok := seen[key]; ok { continue } @@ -116,7 +128,7 @@ func (d *pass) buildMethodImplKeys() { for slotIndex, slot := range slots { id := methodID{owner: typ, slot: slotIndex} for _, iface := range methodRefs[slot.Sig] { - if impls[iface] == len(d.info.InterfaceMethods(iface)) { + if impls[iface] == ifaceMethodCounts[iface] { key := ifaceMethodKey{iface: iface, sig: slot.Sig} d.methodImplKeys[id] = append(d.methodImplKeys[id], key) } diff --git a/internal/deadcode/analyze_test.go b/internal/deadcode/analyze_test.go index 20e1e59ab1..fd8d3cbfc2 100644 --- a/internal/deadcode/analyze_test.go +++ b/internal/deadcode/analyze_test.go @@ -288,6 +288,79 @@ func TestAnalyze(t *testing.T) { "_llgo_pkg.U": {0}, }, }, + // type Type interface{ Elem() Type } + // type rtype struct{} + // func (rtype) Elem() Type { return toType() } + // func main() { init(); toType() } + // func init() { reflectType.Elem() } + // func toType() Type { return rtype{} } + { + name: "interface demand and conversion from different reachable functions meet", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + init := b.Symbol("pkg.init") + toType := b.Symbol("pkg.toType") + typ := b.Symbol("_llgo_pkg.rtype") + iface := b.Symbol("_llgo_pkg.Type") + elemSig := methodSig(b, "Elem") + + b.AddIfaceEntry(iface, []metadata.MethodSig{elemSig}) + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, elemSig, "pkg.(*rtype).Elem", "pkg.rtype.Elem"), + }) + b.AddEdge(main, init) + b.AddEdge(main, toType) + b.AddEdge(toType, typ) + b.AddUseIface(toType, []metadata.Symbol{typ}) + b.AddUseIfaceMethod(init, []metadata.IfaceMethodDemand{{ + Target: iface, + Sig: elemSig, + }}) + }), + want: map[string][]int{"_llgo_pkg.rtype": {0}}, + }, + // type Type interface{ Elem() Type; Kind() Kind } + // type rtype struct{} + // func (rtype) Elem() Type { return toType() } + // func (rtype) Kind() Kind { return 0 } + // func main() { init(); toType() } + // func init() { reflectType.Elem() } + // func toType() Type { return rtype{} } + // + // Some patched packages may report the same interface symbol with an + // alternate signature for one method name. That must not make the + // interface look larger than it is when checking whether rtype implements + // Type. + { + name: "duplicate interface method names do not inflate interface size", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + init := b.Symbol("pkg.init") + toType := b.Symbol("pkg.toType") + typ := b.Symbol("_llgo_pkg.rtype") + iface := b.Symbol("_llgo_pkg.Type") + elemSig := methodSig(b, "Elem") + kindSig := methodSigWithType(b, "Kind", "_llgo_func$kind") + altKindSig := methodSigWithType(b, "Kind", "_llgo_func$altKind") + + b.AddIfaceEntry(iface, []metadata.MethodSig{elemSig, kindSig, altKindSig}) + b.AddMethodInfo(typ, []metadata.MethodSlot{ + methodSlot(b, elemSig, "pkg.(*rtype).Elem", "pkg.rtype.Elem"), + methodSlot(b, kindSig, "pkg.(*rtype).Kind", "pkg.rtype.Kind"), + }) + b.AddEdge(main, init) + b.AddEdge(main, toType) + b.AddEdge(toType, typ) + b.AddUseIface(toType, []metadata.Symbol{typ}) + b.AddUseIfaceMethod(init, []metadata.IfaceMethodDemand{{ + Target: iface, + Sig: elemSig, + }}) + }), + want: map[string][]int{"_llgo_pkg.rtype": {0}}, + }, // type I interface{ M() } // type T struct{} // func (T) M() {} From bb16367dc641b7bb689df6865b1200f08967946d Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 20:40:13 +0800 Subject: [PATCH 09/37] fix deadcode propagation for interface-used type refs --- internal/deadcode/analyze.go | 15 ++++++++++++ internal/deadcode/analyze_test.go | 38 +++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/internal/deadcode/analyze.go b/internal/deadcode/analyze.go index bda2bdc2e5..86f2ec5031 100644 --- a/internal/deadcode/analyze.go +++ b/internal/deadcode/analyze.go @@ -32,6 +32,7 @@ type pass struct { info *metadata.GlobalSummary methodImplKeys map[methodID][]ifaceMethodKey + typeSymbols map[metadata.Symbol]struct{} reachable map[metadata.Symbol]struct{} usedInIface map[metadata.Symbol]struct{} @@ -71,6 +72,7 @@ func deadcode(info *metadata.GlobalSummary, roots []metadata.Symbol) map[metadat d := &pass{ info: info, methodImplKeys: make(map[methodID][]ifaceMethodKey), + typeSymbols: make(map[metadata.Symbol]struct{}), reachable: make(map[metadata.Symbol]struct{}), usedInIface: make(map[metadata.Symbol]struct{}), processedIfaceTy: make(map[metadata.Symbol]struct{}), @@ -98,6 +100,7 @@ func (d *pass) buildMethodImplKeys() { methodRefs := make(map[metadata.MethodSig][]metadata.Symbol) ifaceMethodCounts := make(map[metadata.Symbol]int) for _, iface := range d.info.Interfaces() { + d.typeSymbols[iface] = struct{}{} seenNames := make(map[metadata.Name]struct{}) for _, sig := range d.info.InterfaceMethods(iface) { methodRefs[sig] = appendSymbolUnique(methodRefs[sig], iface) @@ -110,6 +113,7 @@ func (d *pass) buildMethodImplKeys() { } for _, typ := range d.info.ConcreteTypes() { + d.typeSymbols[typ] = struct{}{} slots := d.info.MethodSlots(typ) impls := make(map[metadata.Symbol]int) seen := make(map[ifaceMethodName]struct{}) @@ -145,7 +149,11 @@ func (d *pass) flood() { d.reflectSeen = true } + _, usedInIface := d.usedInIface[sym] for _, dst := range d.info.OrdinaryEdges(sym) { + if usedInIface { + d.markTypeUsedInIface(dst) + } d.markReachable(dst) } @@ -248,6 +256,13 @@ func (d *pass) markUsedInIface(typ metadata.Symbol) { } } +func (d *pass) markTypeUsedInIface(sym metadata.Symbol) { + if _, ok := d.typeSymbols[sym]; !ok { + return + } + d.markUsedInIface(sym) +} + func (d *pass) popWork() metadata.Symbol { sym := d.workQueue[0] copy(d.workQueue, d.workQueue[1:]) diff --git a/internal/deadcode/analyze_test.go b/internal/deadcode/analyze_test.go index fd8d3cbfc2..ae00fbe96f 100644 --- a/internal/deadcode/analyze_test.go +++ b/internal/deadcode/analyze_test.go @@ -179,6 +179,44 @@ func TestAnalyze(t *testing.T) { "_llgo_pkg.T": {0}, }, }, + // type Unmarshaler interface{ UnmarshalJSON([]byte) error } + // type RawMessage []byte + // func (*RawMessage) UnmarshalJSON([]byte) error { return nil } + // type Container struct{ Raw RawMessage } + // func main() { unmarshal(&Container{}) } + // func unmarshal(v any) { field.Addr().Interface().(Unmarshaler).UnmarshalJSON(nil) } + { + name: "value type entering interface semantics keeps pointer method implementation", + roots: []string{"pkg.main"}, + summary: buildPackage(func(b *metadata.Builder) { + main := b.Symbol("pkg.main") + unmarshal := b.Symbol("pkg.unmarshal") + containerPtr := b.Symbol("*_llgo_pkg.Container") + container := b.Symbol("_llgo_pkg.Container") + raw := b.Symbol("_llgo_pkg.RawMessage") + rawPtr := b.Symbol("*_llgo_pkg.RawMessage") + iface := b.Symbol("_llgo_pkg.Unmarshaler") + unmarshalSig := methodSigWithType(b, "UnmarshalJSON", "_llgo_func$bytes_error") + + b.AddIfaceEntry(iface, []metadata.MethodSig{unmarshalSig}) + b.AddMethodInfo(rawPtr, []metadata.MethodSlot{ + methodSlot(b, unmarshalSig, "pkg.(*RawMessage).UnmarshalJSON", "pkg.(*RawMessage).UnmarshalJSON"), + }) + b.AddTypeChild(containerPtr, container) + b.AddTypeChild(container, raw) + b.AddEdge(main, unmarshal) + b.AddEdge(main, containerPtr) + b.AddEdge(containerPtr, container) + b.AddEdge(container, raw) + b.AddEdge(raw, rawPtr) + b.AddUseIface(main, []metadata.Symbol{containerPtr}) + b.AddUseIfaceMethod(unmarshal, []metadata.IfaceMethodDemand{{ + Target: iface, + Sig: unmarshalSig, + }}) + }), + want: map[string][]int{"*_llgo_pkg.RawMessage": {0}}, + }, // type T struct{} // func (T) M() {} // func (T) N() {} From ed040b910c5f316e627d9cc03ff3b8ba3f4a74b8 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 20:45:16 +0800 Subject: [PATCH 10/37] remove cache meta read timing log --- internal/build/build.go | 12 +++++------- internal/build/collect.go | 3 --- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index db2321ad9b..ae3d2f2e7b 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -34,7 +34,6 @@ import ( "strings" "sync" "sync/atomic" - "time" "golang.org/x/tools/go/ssa" @@ -695,7 +694,7 @@ func buildAllPkgs(ctx *context, pkgs []*aPackage, verbose bool) ([]*aPackage, er ctx.tryLoadFromCache(aPkg) if verbose { if aPkg.CacheHit { - fmt.Fprintf(os.Stderr, "CACHE HIT: %s (meta read: %s)\n", pkg.PkgPath, aPkg.MetaReadDuration) + fmt.Fprintf(os.Stderr, "CACHE HIT: %s\n", pkg.PkgPath) } else { fmt.Fprintf(os.Stderr, "CACHE MISS: %s\n", pkg.PkgPath) } @@ -727,7 +726,7 @@ func buildAllPkgs(ctx *context, pkgs []*aPackage, verbose bool) ([]*aPackage, er ctx.tryLoadFromCache(aPkg) if verbose { if aPkg.CacheHit { - fmt.Fprintf(os.Stderr, "CACHE HIT: %s (meta read: %s)\n", pkg.PkgPath, aPkg.MetaReadDuration) + fmt.Fprintf(os.Stderr, "CACHE HIT: %s\n", pkg.PkgPath) } else { fmt.Fprintf(os.Stderr, "CACHE MISS: %s\n", pkg.PkgPath) } @@ -1640,10 +1639,9 @@ type aPackage struct { rewriteVars map[string]string // Cache related fields - Fingerprint string // fingerprint digest - Manifest string // manifest text content - CacheHit bool // whether cache was hit - MetaReadDuration time.Duration + Fingerprint string // fingerprint digest + Manifest string // manifest text content + CacheHit bool // whether cache was hit } type Package = *aPackage diff --git a/internal/build/collect.go b/internal/build/collect.go index d5ce06b7bb..3a991d8b58 100644 --- a/internal/build/collect.go +++ b/internal/build/collect.go @@ -25,7 +25,6 @@ import ( "runtime" "sort" "strings" - "time" "github.com/goplus/llgo/internal/env" "github.com/goplus/llgo/internal/metadata" @@ -341,12 +340,10 @@ func (c *context) tryLoadFromCache(pkg *aPackage) bool { if err != nil { return false } - metaStart := time.Now() pkgMeta, err := readMeta(paths.Meta) if err != nil { return false } - pkg.MetaReadDuration = time.Since(metaStart) // Parse metadata from manifest [Package] section (INI format) meta, err := parseManifestMetadata(content) From 83b0ae003faa2a9096e8daa134fceff01f913b8d Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 20 May 2026 22:01:02 +0800 Subject: [PATCH 11/37] fix dce unreachable method slots --- internal/dcepass/dcepass.go | 18 ++++++++++++++++-- internal/dcepass/dcepass_test.go | 3 +++ runtime/internal/runtime/z_face.go | 8 +++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/internal/dcepass/dcepass.go b/internal/dcepass/dcepass.go index 7b29c9982f..507069fc5a 100644 --- a/internal/dcepass/dcepass.go +++ b/internal/dcepass/dcepass.go @@ -10,6 +10,8 @@ import ( "github.com/xgo-dev/llvm" ) +const unreachableMethodName = "github.com/goplus/llgo/runtime/internal/runtime.unreachableMethod" + // EmitStrongTypeOverrides emits method-pruned strong ABI type symbols into dst. // // srcMods contain the original package modules. For each constant ABI type @@ -88,7 +90,7 @@ func (e *overrideEmitter) emitTypeOverride(srcType, methodsVal llvm.Value, elemT if len(elemFields) < 4 { return fmt.Errorf("method element type has %d fields", len(elemFields)) } - zeroPtr := llvm.ConstPointerNull(elemFields[2]) + unreachableMethod := e.unreachableMethod(elemFields[2]) methodCount := methodsVal.OperandsCount() methods := make([]llvm.Value, methodCount) for i := 0; i < methodCount; i++ { @@ -112,7 +114,7 @@ func (e *overrideEmitter) emitTypeOverride(srcType, methodsVal llvm.Value, elemT if err != nil { return err } - methods[i] = llvm.ConstNamedStruct(elemTy, []llvm.Value{nameField, mtypField, zeroPtr, zeroPtr}) + methods[i] = llvm.ConstNamedStruct(elemTy, []llvm.Value{nameField, mtypField, unreachableMethod, unreachableMethod}) } fields[fieldCount-1] = llvm.ConstArray(elemTy, methods) @@ -123,6 +125,18 @@ func (e *overrideEmitter) emitTypeOverride(srcType, methodsVal llvm.Value, elemT return nil } +func (e *overrideEmitter) unreachableMethod(ptrTy llvm.Type) llvm.Value { + fn := e.dst.NamedFunction(unreachableMethodName) + if fn.IsNil() { + fnTy := llvm.FunctionType(e.dst.Context().VoidType(), nil, false) + fn = llvm.AddFunction(e.dst, unreachableMethodName, fnTy) + } + if fn.Type() == ptrTy { + return fn + } + return llvm.ConstBitCast(fn, ptrTy) +} + func (e *overrideEmitter) ensureOverrideGlobal(src llvm.Value) (llvm.Value, error) { name := src.Name() if name == "" { diff --git a/internal/dcepass/dcepass_test.go b/internal/dcepass/dcepass_test.go index 5db149e32f..38e7456c2c 100644 --- a/internal/dcepass/dcepass_test.go +++ b/internal/dcepass/dcepass_test.go @@ -70,6 +70,9 @@ func TestEmitStrongTypeOverridesPrunesDeadMethodSlots(t *testing.T) { if strings.Contains(out, `ptr @"pkg.(*T).N"`) || strings.Contains(out, `ptr @pkg.T.N`) { t.Fatalf("dead method slot still references N functions:\n%s", out) } + if !strings.Contains(out, `ptr @"github.com/goplus/llgo/runtime/internal/runtime.unreachableMethod"`) { + t.Fatalf("dead method slot was not redirected to unreachableMethod:\n%s", out) + } } func TestEmitStrongTypeOverridesLogsDroppedMethodSlots(t *testing.T) { diff --git a/runtime/internal/runtime/z_face.go b/runtime/internal/runtime/z_face.go index b4df5b96e3..7950e535c3 100644 --- a/runtime/internal/runtime/z_face.go +++ b/runtime/internal/runtime/z_face.go @@ -142,7 +142,7 @@ func NewItab(inter *InterfaceType, typ *Type) *Itab { // matched but no function pointer (e.g. stripped/unreachable after DCE); // keep itab entry with a placeholder so the itab stays intact // and only panics on call, not at assertion time. - fn = abi.Text(uintptr(0)) + fn = abi.Text(c.Func(unreachableMethod)) } *c.Advance(data, i) = uintptr(fn) } @@ -171,6 +171,12 @@ func findMethod(mthds []abi.Method, im abi.Imethod) (abi.Text, bool) { return nil, false } +// unreachableMethod matches Go's runtime.unreachableMethod: method entries +// proven unreachable by link-time DCE are redirected here and should never run. +func unreachableMethod() { + throw("unreachable method called. linker bug?") +} + func IfaceType(i iface) *abi.Type { if i.tab == nil { return nil From ae572d9186fd71bd379ffb8bce5f990537db3306 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Thu, 21 May 2026 14:17:16 +0800 Subject: [PATCH 12/37] fix dce runtime init root --- internal/build/build.go | 14 +++++++++----- internal/build/dce_override_test.go | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index ae3d2f2e7b..8ebadf0759 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1033,7 +1033,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa abiSymbols: linkedModuleGlobals(linkedOrder), }) if ctx.buildConf.DCE { - if err := applyDCEOverrides(ctx, pkg, linkedOrder, entryPkg, verbose); err != nil { + if err := applyDCEOverrides(ctx, pkg, linkedOrder, entryPkg, needRuntime, verbose); err != nil { return err } } @@ -1092,7 +1092,7 @@ func linkedModuleGlobals(pkgs []Package) map[string]none { return seen } -func applyDCEOverrides(ctx *context, mainPkg *packages.Package, pkgs []Package, entryPkg Package, verbose bool) error { +func applyDCEOverrides(ctx *context, mainPkg *packages.Package, pkgs []Package, entryPkg Package, needRuntime bool, verbose bool) error { metas := linkedPackageMetas(pkgs) if len(metas) == 0 { return nil @@ -1101,7 +1101,7 @@ func applyDCEOverrides(ctx *context, mainPkg *packages.Package, pkgs []Package, if err != nil { return err } - roots := dceEntryRootCandidates(mainPkg) + roots := dceEntryRootCandidates(mainPkg, needRuntime) liveSlots := deadcode.Analyze(summary, roots) if len(liveSlots) == 0 { return nil @@ -1145,11 +1145,15 @@ func dceSourceModules(pkgs []Package) []gllvm.Module { return mods } -func dceEntryRootCandidates(pkg *packages.Package) []string { +func dceEntryRootCandidates(pkg *packages.Package, needRuntime bool) []string { if pkg == nil || pkg.PkgPath == "" { return nil } - return []string{pkg.PkgPath + ".init", pkg.PkgPath + ".main"} + roots := []string{pkg.PkgPath + ".init", pkg.PkgPath + ".main"} + if needRuntime { + roots = append(roots, llssa.PkgRuntime+".init") + } + return roots } // isRuntimePkg reports whether the package path belongs to the llgo runtime tree. diff --git a/internal/build/dce_override_test.go b/internal/build/dce_override_test.go index 9a4247b0a7..3741f25551 100644 --- a/internal/build/dce_override_test.go +++ b/internal/build/dce_override_test.go @@ -40,7 +40,7 @@ func TestApplyDCEOverridesWritesStrongTypeOverride(t *testing.T) { ExportFile: "pkg.a", }, &genConfig{}) - if err := applyDCEOverrides(ctx, &packages.Package{PkgPath: "pkg"}, []Package{srcAPkg}, entryPkg, false); err != nil { + if err := applyDCEOverrides(ctx, &packages.Package{PkgPath: "pkg"}, []Package{srcAPkg}, entryPkg, false, false); err != nil { t.Fatalf("applyDCEOverrides: %v", err) } @@ -56,6 +56,22 @@ func TestApplyDCEOverridesWritesStrongTypeOverride(t *testing.T) { } } +func TestDCEEntryRootCandidatesIncludesRuntimeWhenNeeded(t *testing.T) { + roots := dceEntryRootCandidates(&packages.Package{PkgPath: "pkg"}, true) + want := []string{"pkg.init", "pkg.main", llssa.PkgRuntime + ".init"} + if strings.Join(roots, "\n") != strings.Join(want, "\n") { + t.Fatalf("roots mismatch:\ngot %q\nwant %q", roots, want) + } +} + +func TestDCEEntryRootCandidatesSkipsRuntimeWhenNotNeeded(t *testing.T) { + roots := dceEntryRootCandidates(&packages.Package{PkgPath: "pkg"}, false) + want := []string{"pkg.init", "pkg.main"} + if strings.Join(roots, "\n") != strings.Join(want, "\n") { + t.Fatalf("roots mismatch:\ngot %q\nwant %q", roots, want) + } +} + func buildDCEMeta() *metadata.PackageMeta { b := metadata.NewBuilder() main := b.Symbol("pkg.main") From dfb4ae298182ca29bb5617392c6fe8eac980676c Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sun, 21 Jun 2026 17:12:57 +0800 Subject: [PATCH 13/37] test: update metadata expectations for method stubs --- .../interface_anyonmous/meta-expect.txt | 20 +++- cl/_testmeta/interface_named/meta-expect.txt | 10 +- .../interface_unexported/meta-expect.txt | 10 +- .../methodinfo_imported/meta-expect.txt | 108 +++++++++++++----- cl/_testmeta/reflect_dynamic/meta-expect.txt | 10 +- cl/_testmeta/reflect_named/meta-expect.txt | 20 +++- 6 files changed, 137 insertions(+), 41 deletions(-) diff --git a/cl/_testmeta/interface_anyonmous/meta-expect.txt b/cl/_testmeta/interface_anyonmous/meta-expect.txt index a915573fce..8dec3da93c 100644 --- a/cl/_testmeta/interface_anyonmous/meta-expect.txt +++ b/cl/_testmeta/interface_anyonmous/meta-expect.txt @@ -23,6 +23,14 @@ _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: *_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N: + github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: github.com/goplus/llgo/runtime/internal/runtime.interequal __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: @@ -41,8 +49,12 @@ _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo$imethods: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M: + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N: + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.init: github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.init$guard @@ -66,9 +78,9 @@ github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.use: [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M - 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M + 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M - 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M + 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index b341d5d3c4..76c246d900 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -22,6 +22,10 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: *_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: + github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M: + github.com/goplus/llgo/cl/_testmeta/interface_named.T.M __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: github.com/goplus/llgo/runtime/internal/runtime.interequal __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: @@ -40,6 +44,8 @@ _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/interface_named.T.M github.com/goplus/llgo/cl/_testmeta/interface_named.init: github.com/goplus/llgo/cl/_testmeta/interface_named.init$guard @@ -62,7 +68,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_named.use: [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_named.T.M + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index 5331fd5455..834636e0d1 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -22,6 +22,10 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: *github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: github.com/goplus/llgo/runtime/internal/runtime.interequal __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: @@ -34,6 +38,8 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal @@ -62,7 +68,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: - 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m + 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: - 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m + 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index 0f4458a5b0..68f003717a 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -245,6 +245,60 @@ _llgo_io.Reader: []_llgo_uint8: *[]_llgo_uint8 _llgo_uint8 +__llgo_stub.bytes.(*Buffer).Available: + bytes.(*Buffer).Available +__llgo_stub.bytes.(*Buffer).AvailableBuffer: + bytes.(*Buffer).AvailableBuffer +__llgo_stub.bytes.(*Buffer).Bytes: + bytes.(*Buffer).Bytes +__llgo_stub.bytes.(*Buffer).Cap: + bytes.(*Buffer).Cap +__llgo_stub.bytes.(*Buffer).Grow: + bytes.(*Buffer).Grow +__llgo_stub.bytes.(*Buffer).Len: + bytes.(*Buffer).Len +__llgo_stub.bytes.(*Buffer).Next: + bytes.(*Buffer).Next +__llgo_stub.bytes.(*Buffer).Read: + bytes.(*Buffer).Read +__llgo_stub.bytes.(*Buffer).ReadByte: + bytes.(*Buffer).ReadByte +__llgo_stub.bytes.(*Buffer).ReadBytes: + bytes.(*Buffer).ReadBytes +__llgo_stub.bytes.(*Buffer).ReadFrom: + bytes.(*Buffer).ReadFrom +__llgo_stub.bytes.(*Buffer).ReadRune: + bytes.(*Buffer).ReadRune +__llgo_stub.bytes.(*Buffer).ReadString: + bytes.(*Buffer).ReadString +__llgo_stub.bytes.(*Buffer).Reset: + bytes.(*Buffer).Reset +__llgo_stub.bytes.(*Buffer).String: + bytes.(*Buffer).String +__llgo_stub.bytes.(*Buffer).Truncate: + bytes.(*Buffer).Truncate +__llgo_stub.bytes.(*Buffer).UnreadByte: + bytes.(*Buffer).UnreadByte +__llgo_stub.bytes.(*Buffer).UnreadRune: + bytes.(*Buffer).UnreadRune +__llgo_stub.bytes.(*Buffer).Write: + bytes.(*Buffer).Write +__llgo_stub.bytes.(*Buffer).WriteByte: + bytes.(*Buffer).WriteByte +__llgo_stub.bytes.(*Buffer).WriteRune: + bytes.(*Buffer).WriteRune +__llgo_stub.bytes.(*Buffer).WriteString: + bytes.(*Buffer).WriteString +__llgo_stub.bytes.(*Buffer).WriteTo: + bytes.(*Buffer).WriteTo +__llgo_stub.bytes.(*Buffer).empty: + bytes.(*Buffer).empty +__llgo_stub.bytes.(*Buffer).grow: + bytes.(*Buffer).grow +__llgo_stub.bytes.(*Buffer).readSlice: + bytes.(*Buffer).readSlice +__llgo_stub.bytes.(*Buffer).tryGrowByReslice: + bytes.(*Buffer).tryGrowByReslice __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: github.com/goplus/llgo/runtime/internal/runtime.interequal __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32: @@ -469,31 +523,31 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: [MethodInfo] *_llgo_bytes.Buffer: - 0 Available _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Available bytes.(*Buffer).Available - 1 AvailableBuffer _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY bytes.(*Buffer).AvailableBuffer bytes.(*Buffer).AvailableBuffer - 2 Bytes _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY bytes.(*Buffer).Bytes bytes.(*Buffer).Bytes - 3 Cap _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Cap bytes.(*Buffer).Cap - 4 Grow _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA bytes.(*Buffer).Grow bytes.(*Buffer).Grow - 5 Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Len bytes.(*Buffer).Len - 6 Next _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY bytes.(*Buffer).Next bytes.(*Buffer).Next - 7 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk bytes.(*Buffer).Read bytes.(*Buffer).Read - 8 ReadByte _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ bytes.(*Buffer).ReadByte bytes.(*Buffer).ReadByte - 9 ReadBytes _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 bytes.(*Buffer).ReadBytes bytes.(*Buffer).ReadBytes - 10 ReadFrom _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 bytes.(*Buffer).ReadFrom bytes.(*Buffer).ReadFrom - 11 ReadRune _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y bytes.(*Buffer).ReadRune bytes.(*Buffer).ReadRune - 12 ReadString _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o bytes.(*Buffer).ReadString bytes.(*Buffer).ReadString - 13 Reset _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac bytes.(*Buffer).Reset bytes.(*Buffer).Reset - 14 String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to bytes.(*Buffer).String bytes.(*Buffer).String - 15 Truncate _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA bytes.(*Buffer).Truncate bytes.(*Buffer).Truncate - 16 UnreadByte _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w bytes.(*Buffer).UnreadByte bytes.(*Buffer).UnreadByte - 17 UnreadRune _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w bytes.(*Buffer).UnreadRune bytes.(*Buffer).UnreadRune - 18 Write _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk bytes.(*Buffer).Write bytes.(*Buffer).Write - 19 WriteByte _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg bytes.(*Buffer).WriteByte bytes.(*Buffer).WriteByte - 20 WriteRune _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw bytes.(*Buffer).WriteRune bytes.(*Buffer).WriteRune - 21 WriteString _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw bytes.(*Buffer).WriteString bytes.(*Buffer).WriteString - 22 WriteTo _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M bytes.(*Buffer).WriteTo bytes.(*Buffer).WriteTo - 23 bytes.empty _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk bytes.(*Buffer).empty bytes.(*Buffer).empty - 24 bytes.grow _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU bytes.(*Buffer).grow bytes.(*Buffer).grow - 25 bytes.readSlice _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 bytes.(*Buffer).readSlice bytes.(*Buffer).readSlice - 26 bytes.tryGrowByReslice _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M bytes.(*Buffer).tryGrowByReslice bytes.(*Buffer).tryGrowByReslice + 0 Available _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Available __llgo_stub.bytes.(*Buffer).Available + 1 AvailableBuffer _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY bytes.(*Buffer).AvailableBuffer __llgo_stub.bytes.(*Buffer).AvailableBuffer + 2 Bytes _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY bytes.(*Buffer).Bytes __llgo_stub.bytes.(*Buffer).Bytes + 3 Cap _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Cap __llgo_stub.bytes.(*Buffer).Cap + 4 Grow _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA bytes.(*Buffer).Grow __llgo_stub.bytes.(*Buffer).Grow + 5 Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA bytes.(*Buffer).Len __llgo_stub.bytes.(*Buffer).Len + 6 Next _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY bytes.(*Buffer).Next __llgo_stub.bytes.(*Buffer).Next + 7 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk bytes.(*Buffer).Read __llgo_stub.bytes.(*Buffer).Read + 8 ReadByte _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ bytes.(*Buffer).ReadByte __llgo_stub.bytes.(*Buffer).ReadByte + 9 ReadBytes _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 bytes.(*Buffer).ReadBytes __llgo_stub.bytes.(*Buffer).ReadBytes + 10 ReadFrom _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 bytes.(*Buffer).ReadFrom __llgo_stub.bytes.(*Buffer).ReadFrom + 11 ReadRune _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y bytes.(*Buffer).ReadRune __llgo_stub.bytes.(*Buffer).ReadRune + 12 ReadString _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o bytes.(*Buffer).ReadString __llgo_stub.bytes.(*Buffer).ReadString + 13 Reset _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac bytes.(*Buffer).Reset __llgo_stub.bytes.(*Buffer).Reset + 14 String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to bytes.(*Buffer).String __llgo_stub.bytes.(*Buffer).String + 15 Truncate _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA bytes.(*Buffer).Truncate __llgo_stub.bytes.(*Buffer).Truncate + 16 UnreadByte _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w bytes.(*Buffer).UnreadByte __llgo_stub.bytes.(*Buffer).UnreadByte + 17 UnreadRune _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w bytes.(*Buffer).UnreadRune __llgo_stub.bytes.(*Buffer).UnreadRune + 18 Write _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk bytes.(*Buffer).Write __llgo_stub.bytes.(*Buffer).Write + 19 WriteByte _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg bytes.(*Buffer).WriteByte __llgo_stub.bytes.(*Buffer).WriteByte + 20 WriteRune _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw bytes.(*Buffer).WriteRune __llgo_stub.bytes.(*Buffer).WriteRune + 21 WriteString _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw bytes.(*Buffer).WriteString __llgo_stub.bytes.(*Buffer).WriteString + 22 WriteTo _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M bytes.(*Buffer).WriteTo __llgo_stub.bytes.(*Buffer).WriteTo + 23 bytes.empty _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk bytes.(*Buffer).empty __llgo_stub.bytes.(*Buffer).empty + 24 bytes.grow _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU bytes.(*Buffer).grow __llgo_stub.bytes.(*Buffer).grow + 25 bytes.readSlice _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 bytes.(*Buffer).readSlice __llgo_stub.bytes.(*Buffer).readSlice + 26 bytes.tryGrowByReslice _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M bytes.(*Buffer).tryGrowByReslice __llgo_stub.bytes.(*Buffer).tryGrowByReslice diff --git a/cl/_testmeta/reflect_dynamic/meta-expect.txt b/cl/_testmeta/reflect_dynamic/meta-expect.txt index 5de18338cb..e404868323 100644 --- a/cl/_testmeta/reflect_dynamic/meta-expect.txt +++ b/cl/_testmeta/reflect_dynamic/meta-expect.txt @@ -11,6 +11,10 @@ *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M: + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: github.com/goplus/llgo/runtime/internal/runtime.memequal0 __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: @@ -21,6 +25,8 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M: + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.init: github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.init$guard @@ -39,9 +45,9 @@ github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M [ReflectMethod] github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index 73912323fb..70d6b07587 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -51,6 +51,14 @@ _llgo_reflect.Type: *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M: + github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m: + github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M: + github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m: + github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: github.com/goplus/llgo/runtime/internal/runtime.memequal0 __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: @@ -61,8 +69,12 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M: + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m: + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m github.com/goplus/llgo/cl/_testmeta/reflect_named.init: github.com/goplus/llgo/cl/_testmeta/reflect_named.init$guard @@ -83,11 +95,11 @@ github.com/goplus/llgo/cl/_testmeta/reflect_named.main: [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M - 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M + 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M - 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m + 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M + 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m [UseNamedMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: From 40a546b37ba518ff278a878a3140efbdc7fcef01 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sun, 21 Jun 2026 19:04:53 +0800 Subject: [PATCH 14/37] fix: record interface use from pointer boxing --- cl/_testmeta/interface_exported_var/in.go | 8 + .../interface_exported_var/meta-expect.txt | 357 ++++++++++++++++++ ssa/interface.go | 17 +- 3 files changed, 376 insertions(+), 6 deletions(-) create mode 100644 cl/_testmeta/interface_exported_var/in.go create mode 100644 cl/_testmeta/interface_exported_var/meta-expect.txt diff --git a/cl/_testmeta/interface_exported_var/in.go b/cl/_testmeta/interface_exported_var/in.go new file mode 100644 index 0000000000..b8ab613bac --- /dev/null +++ b/cl/_testmeta/interface_exported_var/in.go @@ -0,0 +1,8 @@ +package main + +import "encoding/binary" + +func main() { + var order binary.ByteOrder = binary.LittleEndian + _ = order.Uint16([]byte{1, 2}) +} diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt new file mode 100644 index 0000000000..97affb155a --- /dev/null +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -0,0 +1,357 @@ +[TypeChildren] +*[]_llgo_uint8: + []_llgo_uint8 +*_llgo_encoding/binary.littleEndian: + _llgo_encoding/binary.littleEndian +*_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: + _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs +*_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc: + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc +*_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw: + _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw +*_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg: + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg +*_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc: + _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc +*_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus: + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus +*_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU: + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU +*_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8: + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 +*_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw +*_llgo_string: + _llgo_string +*_llgo_uint16: + _llgo_uint16 +*_llgo_uint32: + _llgo_uint32 +*_llgo_uint64: + _llgo_uint64 +*_llgo_uint8: + _llgo_uint8 +[]_llgo_uint8: + _llgo_uint8 +_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_string +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + +[InterfaceInfo] +_llgo_encoding/binary.ByteOrder: + PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + +[OrdinaryEdges] +*[]_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + []_llgo_uint8 +*_llgo_encoding/binary.littleEndian: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_encoding/binary.littleEndian +*_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs +*_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc +*_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw +*_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg +*_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc +*_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus +*_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU +*_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 +*_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +*_llgo_uint16: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint16 +*_llgo_uint32: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint32 +*_llgo_uint64: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint64 +*_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint8 +[]_llgo_uint8: + *[]_llgo_uint8 + _llgo_uint8 +__llgo_stub.encoding/binary.(*littleEndian).AppendUint16: + encoding/binary.(*littleEndian).AppendUint16 +__llgo_stub.encoding/binary.(*littleEndian).AppendUint32: + encoding/binary.(*littleEndian).AppendUint32 +__llgo_stub.encoding/binary.(*littleEndian).AppendUint64: + encoding/binary.(*littleEndian).AppendUint64 +__llgo_stub.encoding/binary.(*littleEndian).GoString: + encoding/binary.(*littleEndian).GoString +__llgo_stub.encoding/binary.(*littleEndian).PutUint16: + encoding/binary.(*littleEndian).PutUint16 +__llgo_stub.encoding/binary.(*littleEndian).PutUint32: + encoding/binary.(*littleEndian).PutUint32 +__llgo_stub.encoding/binary.(*littleEndian).PutUint64: + encoding/binary.(*littleEndian).PutUint64 +__llgo_stub.encoding/binary.(*littleEndian).String: + encoding/binary.(*littleEndian).String +__llgo_stub.encoding/binary.(*littleEndian).Uint16: + encoding/binary.(*littleEndian).Uint16 +__llgo_stub.encoding/binary.(*littleEndian).Uint32: + encoding/binary.(*littleEndian).Uint32 +__llgo_stub.encoding/binary.(*littleEndian).Uint64: + encoding/binary.(*littleEndian).Uint64 +__llgo_stub.encoding/binary.littleEndian.AppendUint16: + encoding/binary.littleEndian.AppendUint16 +__llgo_stub.encoding/binary.littleEndian.AppendUint32: + encoding/binary.littleEndian.AppendUint32 +__llgo_stub.encoding/binary.littleEndian.AppendUint64: + encoding/binary.littleEndian.AppendUint64 +__llgo_stub.encoding/binary.littleEndian.GoString: + encoding/binary.littleEndian.GoString +__llgo_stub.encoding/binary.littleEndian.PutUint16: + encoding/binary.littleEndian.PutUint16 +__llgo_stub.encoding/binary.littleEndian.PutUint32: + encoding/binary.littleEndian.PutUint32 +__llgo_stub.encoding/binary.littleEndian.PutUint64: + encoding/binary.littleEndian.PutUint64 +__llgo_stub.encoding/binary.littleEndian.String: + encoding/binary.littleEndian.String +__llgo_stub.encoding/binary.littleEndian.Uint16: + encoding/binary.littleEndian.Uint16 +__llgo_stub.encoding/binary.littleEndian.Uint32: + encoding/binary.littleEndian.Uint32 +__llgo_stub.encoding/binary.littleEndian.Uint64: + encoding/binary.littleEndian.Uint64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal16: + github.com/goplus/llgo/runtime/internal/runtime.memequal16 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32: + github.com/goplus/llgo/runtime/internal/runtime.memequal32 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8: + github.com/goplus/llgo/runtime/internal/runtime.memequal8 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_encoding/binary.littleEndian: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 + *_llgo_encoding/binary.littleEndian +_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: + *_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs + _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs$in + _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs$out +_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs$in: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs$out: + []_llgo_uint8 +_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc: + *_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc$in + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc$out +_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc$in: + []_llgo_uint8 +_llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc$out: + _llgo_uint32 +_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw: + *_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw + _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw$in + _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw$out +_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw$in: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw$out: + []_llgo_uint8 +_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg: + *_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg$in +_llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg$in: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc: + *_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc + _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc$in + _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc$out +_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc$in: + []_llgo_uint8 + _llgo_uint32 +_llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc$out: + []_llgo_uint8 +_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus: + *_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus$in + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus$out +_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus$in: + []_llgo_uint8 +_llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus$out: + _llgo_uint16 +_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU: + *_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU$in +_llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU$in: + []_llgo_uint8 + _llgo_uint16 +_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8: + *_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8$in +_llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8$in: + []_llgo_uint8 + _llgo_uint64 +_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: + *_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE$in + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE$out +_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE$in: + []_llgo_uint8 +_llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE$out: + _llgo_uint64 +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: + _llgo_string +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + *_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods: + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE +_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal + *_llgo_string +_llgo_uint16: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal16 + *_llgo_uint16 +_llgo_uint32: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32 + *_llgo_uint32 +_llgo_uint64: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 + *_llgo_uint64 +_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 + *_llgo_uint8 +github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init: + github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init$guard + encoding/binary.init +github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.AllocU + _llgo_encoding/binary.littleEndian + encoding/binary.LittleEndian + github.com/goplus/llgo/runtime/internal/runtime.Typedmemmove + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw + github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/runtime/internal/runtime.AllocZ + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: + _llgo_encoding/binary.littleEndian + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: + _llgo_encoding/binary.ByteOrder Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + +[MethodInfo] +*_llgo_encoding/binary.littleEndian: + 0 AppendUint16 _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw encoding/binary.(*littleEndian).AppendUint16 __llgo_stub.encoding/binary.(*littleEndian).AppendUint16 + 1 AppendUint32 _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc encoding/binary.(*littleEndian).AppendUint32 __llgo_stub.encoding/binary.(*littleEndian).AppendUint32 + 2 AppendUint64 _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs encoding/binary.(*littleEndian).AppendUint64 __llgo_stub.encoding/binary.(*littleEndian).AppendUint64 + 3 GoString _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to encoding/binary.(*littleEndian).GoString __llgo_stub.encoding/binary.(*littleEndian).GoString + 4 PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU encoding/binary.(*littleEndian).PutUint16 __llgo_stub.encoding/binary.(*littleEndian).PutUint16 + 5 PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg encoding/binary.(*littleEndian).PutUint32 __llgo_stub.encoding/binary.(*littleEndian).PutUint32 + 6 PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 encoding/binary.(*littleEndian).PutUint64 __llgo_stub.encoding/binary.(*littleEndian).PutUint64 + 7 String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to encoding/binary.(*littleEndian).String __llgo_stub.encoding/binary.(*littleEndian).String + 8 Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus encoding/binary.(*littleEndian).Uint16 __llgo_stub.encoding/binary.(*littleEndian).Uint16 + 9 Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc encoding/binary.(*littleEndian).Uint32 __llgo_stub.encoding/binary.(*littleEndian).Uint32 + 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.(*littleEndian).Uint64 +_llgo_encoding/binary.littleEndian: + 0 AppendUint16 _llgo_func$JXgl4jz35cOkksEkyj-ImVTqIDNL16JrZ25HHa-Yekw encoding/binary.(*littleEndian).AppendUint16 __llgo_stub.encoding/binary.littleEndian.AppendUint16 + 1 AppendUint32 _llgo_func$_JswjMs_mFNKWtFb56TJlZa479nBYWhoAxYBwUTwOyc encoding/binary.(*littleEndian).AppendUint32 __llgo_stub.encoding/binary.littleEndian.AppendUint32 + 2 AppendUint64 _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs encoding/binary.(*littleEndian).AppendUint64 __llgo_stub.encoding/binary.littleEndian.AppendUint64 + 3 GoString _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to encoding/binary.(*littleEndian).GoString __llgo_stub.encoding/binary.littleEndian.GoString + 4 PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU encoding/binary.(*littleEndian).PutUint16 __llgo_stub.encoding/binary.littleEndian.PutUint16 + 5 PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg encoding/binary.(*littleEndian).PutUint32 __llgo_stub.encoding/binary.littleEndian.PutUint32 + 6 PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 encoding/binary.(*littleEndian).PutUint64 __llgo_stub.encoding/binary.littleEndian.PutUint64 + 7 String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to encoding/binary.(*littleEndian).String __llgo_stub.encoding/binary.littleEndian.String + 8 Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus encoding/binary.(*littleEndian).Uint16 __llgo_stub.encoding/binary.littleEndian.Uint16 + 9 Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc encoding/binary.(*littleEndian).Uint32 __llgo_stub.encoding/binary.littleEndian.Uint32 + 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 + diff --git a/ssa/interface.go b/ssa/interface.go index f5b90d0b4d..c120e24b4b 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -137,12 +137,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { } prog := b.Prog typ := x.Type - if mb := b.Pkg.MetaBuilder; mb != nil { - if _, ok := types.Unalias(typ.raw.Type).Underlying().(*types.Interface); !ok { - typeName, _ := prog.abi.TypeName(typ.raw.Type) - mb.AddUseIface(mb.Symbol(b.Func.Name()), []metadata.Symbol{mb.Symbol(typeName)}) - } - } + b.recordUseIface(typ) tabi := b.abiType(typ.raw.Type) if !directIfaceType(typ.raw.Type) { vptr := b.AllocU(typ) @@ -192,6 +187,7 @@ func (b Builder) MakeInterfaceFromPtr(tinter Type, ptr Expr) (ret Expr) { return b.MakeInterface(tinter, b.Load(ptr)) } + b.recordUseIface(typ) vptr := b.AllocU(typ) dst := b.Convert(prog.VoidPtr(), vptr) src := b.Convert(prog.VoidPtr(), ptr) @@ -199,6 +195,15 @@ func (b Builder) MakeInterfaceFromPtr(tinter Type, ptr Expr) (ret Expr) { return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter} } +func (b Builder) recordUseIface(typ Type) { + if mb := b.Pkg.MetaBuilder; mb != nil { + if _, ok := types.Unalias(typ.raw.Type).Underlying().(*types.Interface); !ok { + typeName, _ := b.Prog.abi.TypeName(typ.raw.Type) + mb.AddUseIface(mb.Symbol(b.Func.Name()), []metadata.Symbol{mb.Symbol(typeName)}) + } + } +} + func (b Builder) valFromData(typ Type, data llvm.Value) Expr { prog := b.Prog if !directIfaceType(typ.raw.Type) { From 21d5f0186cea063bab20cc0e50d77dc275c7298b Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 26 Jun 2026 23:08:52 +0800 Subject: [PATCH 15/37] feat: add internal/meta package with mmap-based package summary cache format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces internal/meta, a new binary format and read/write layer for LLGo's whole-program DCE package summary cache (.meta files). Key design decisions: - Zero-copy reads via mmap: PackageMeta is a thin view over a mmap'd byte slice; Edges, TypeChildren, MethodSlots and IfaceMethods all use unsafe.Slice to reinterpret the on-disk bytes directly, with no alloc. - CSR layout (Compressed Sparse Row): each per-symbol section stores an offsets[nsyms+1] prefix array and a flat data array, matching the design used by Go's own goobj format (BlkRelocIdx + BlkReloc). - Single allocation on write: Builder.Build() pre-computes all section sizes and offsets in one pass, allocates one []byte, then writes every section directly at its target offset — no intermediate buffers, no seek-back. - Lazy remap in GlobalSummary: merging N PackageMetas only interns the string tables (O(unique_symbols)); edges and type-children are translated lazily on query, avoiding the O(total_edges) rewrite cost of the MVP. - NameRef for method names: method short names are stored as {Off, Len} pairs referencing the string table, keeping them distinct from module-level symbols (LocalSymbol) while still enabling cross-package matching via global Name interning in GlobalSummary. - Compile-time layout assertions: const expressions verify sizeof(Edge)==12, sizeof(MethodSlot)==20, sizeof(MethodSig)==12 so any struct drift that would break the zero-copy reinterpretation fails at build time. Sections in the .meta wire format: stringTable | Symbols | Edges (CSR) | TypeChildren (CSR) | MethodInfo (CSR) | InterfaceInfo (CSR) | ReflectBitmap Co-Authored-By: Claude Sonnet 4.6 --- doc/package-summary-cache-format.md | 447 ++++++++++++++++++++++++++++ internal/meta/build.go | 248 +++++++++++++++ internal/meta/builder.go | 189 ++++++++++++ internal/meta/global.go | 327 ++++++++++++++++++++ internal/meta/global_test.go | 173 +++++++++++ internal/meta/meta.go | 283 ++++++++++++++++++ internal/meta/meta_test.go | 263 ++++++++++++++++ internal/meta/types.go | 51 ++++ 8 files changed, 1981 insertions(+) create mode 100644 doc/package-summary-cache-format.md create mode 100644 internal/meta/build.go create mode 100644 internal/meta/builder.go create mode 100644 internal/meta/global.go create mode 100644 internal/meta/global_test.go create mode 100644 internal/meta/meta.go create mode 100644 internal/meta/meta_test.go create mode 100644 internal/meta/types.go diff --git a/doc/package-summary-cache-format.md b/doc/package-summary-cache-format.md new file mode 100644 index 0000000000..62bcf003aa --- /dev/null +++ b/doc/package-summary-cache-format.md @@ -0,0 +1,447 @@ +# LLGo Package Summary Cache Format + +## 概述 + +本文档定义 LLGo 包级摘要缓存(Package Summary Cache)的二进制文件格式。该格式服务于全图方法可达性分析(Whole-Program DCE),其核心设计目标是: + +- **零反序列化**:文件通过 mmap 映射到内存,字节直接作为内存对象访问 +- **O(1) 查询**:所有 per-symbol 数据通过 CSR 布局实现直接寻址 +- **极低合并开销**:GlobalSummary 合并阶段只 intern 字符串表,不遍历重写所有边 + +设计参考了 Go 官方 linker(`cmd/internal/goobj`)的 `RelocIndex + Reloc` CSR 布局,并结合 LLGo 的实际需求做了简化。 + +--- + +## 文件布局 + +``` +┌──────────────────────────────────────────────────────┐ +│ Header │ +├──────────────────────────────────────────────────────┤ +│ stringTable │ +├──────────────────────────────────────────────────────┤ +│ Symbols │ +├──────────────────────────────────────────────────────┤ +│ Edges (CSR) │ +├──────────────────────────────────────────────────────┤ +│ TypeChildren (CSR) │ +├──────────────────────────────────────────────────────┤ +│ MethodInfo (CSR) │ +├──────────────────────────────────────────────────────┤ +│ InterfaceInfo (CSR) │ +├──────────────────────────────────────────────────────┤ +│ ReflectBitmap │ +└──────────────────────────────────────────────────────┘ +``` + +所有整数均为 **little-endian**。所有 section 4 字节对齐。 + +--- + +## Header + +``` +Header { + Magic [4]byte // "LLPM" + Version uint32 // 当前版本 = 1 + SectionOffsets [8]uint32 // 各 section 在文件中的起始字节位置 + // [0]=stringTable [1]=Symbols [2]=Edges + // [3]=TypeChildren [4]=MethodInfo + // [5]=InterfaceInfo [6]=ReflectBitmap [7]=reserved +} +``` + +Header 总大小固定 = 4 + 4 + 8×4 = **40 字节**。 + +`SectionOffsets` 让读取方能直接跳到任意 section,无需顺序扫描。 + +--- + +## stringTable section + +``` +stringTable { + data []byte // 所有字符串连续拼接的原始字节流 +} +``` + +字符串不做任何结构化:直接把所有符号名拼接在一起。 +`Symbols` section 里的每条 record 通过 `(NameOff, NameLen)` 引用对应字节区间。 + +--- + +## Symbols section + +``` +Symbols { + NSyms uint32 + Records [NSyms]SymbolRecord +} + +SymbolRecord { // 12 字节定长 + NameOff uint32 // 符号名在 stringTable 中的起始偏移 + NameLen uint32 // 符号名长度(字节) + _ [4]byte // 保留,对齐到 12 字节 +} +``` + +**LocalSymbol = Records 数组的下标(uint32)**,在所有其他 section 中统一使用。 + +### 设计要点 + +- **内外部符号不做区分**:无论是本包定义的还是外部包引用的符号,都在此表中分配 LocalSymbol,写法完全一致。 +- **不存 flag/属性**:符号的语义属性(是否是类型、是否是接口)通过它在哪些 section 中出现来隐式表达(见「符号语义识别」章节)。 +- **幂等注册**:Builder 阶段同一个名字多次调用 `Symbol()` 返回相同的 LocalSymbol。 + +--- + +## CSR 布局(通用) + +Edges、TypeChildren、MethodInfo、InterfaceInfo 四个 section 均采用 CSR(Compressed Sparse Row)布局: + +``` +{SectionName} { + NSyms uint32 // 符号数量(= Symbols.NSyms) + Offsets [NSyms+1]uint32 // CSR offsets 数组,Offsets[NSyms] 为尾哨兵 + Data [...]T // 定长 record 的连续数组,T 因 section 而异 +} +``` + +**查询 symbol i 的数据**: + +``` +start = Offsets[i] +end = Offsets[i+1] +result = Data[start : end] // 零拷贝切片 +``` + +若 `start == end`,该符号在此 section 中无数据(正常情况,不代表错误)。 + +每个符号在 Offsets 数组中有且仅有一个 entry,**包括没有数据的符号**(它们的相邻 offset 相等)。这是 Go linker 的标准做法,每符号成本为 4 字节(一个 uint32),简单且查询 O(1)。 + +--- + +## Edges section + +``` +Edges { + NSyms uint32 + Offsets [NSyms+1]uint32 + Data []Edge +} + +Edge { // 12 字节定长 + Target uint32 // LocalSymbol(EdgeOrdinary/UseIface/UseIfaceMethod) + // 或 Name 引用(UseNamedMethod,此时为 stringTable offset) + Extra uint32 // 仅 UseIfaceMethod 使用:目标接口的方法 index + // 其他 Kind 为 0 + Kind uint8 // 边的种类(见下表) + _ [3]byte // padding +} +``` + +### Kind 定义 + +| Kind | 值 | 含义 | Target | Extra | +|------|---|------|--------|-------| +| `EdgeOrdinary` | 0 | 普通符号引用 | 目标 LocalSymbol | 0 | +| `EdgeUseIface` | 1 | 该函数将 Target 类型转为接口 | 类型 LocalSymbol | 0 | +| `EdgeUseIfaceMethod` | 2 | 该函数调用了 Target 接口的某个方法 | 接口 LocalSymbol | 方法 index | +| `EdgeUseNamedMethod` | 3 | 该函数按名调用方法(MethodByName 常量) | stringTable offset | 0 | + +### 说明 + +- `EdgeOrdinary` 涵盖所有普通符号引用:函数调用、全局变量引用、类型描述符引用等。 +- OrdinaryEdges 来自两个来源:cl/ssa 编译阶段(类型/接口相关事实)和 build 层扫描 LLVM Module 指令(普通调用/引用关系)。 +- 对应 Go linker 中 `R_CALL`、`R_ADDR` 等普通 reloc 类型(EdgeOrdinary),以及 `R_USEIFACE`、`R_USEIFACEMETHOD`、`R_USENAMEDMETHOD` marker reloc(其余三种 Kind)。 + +--- + +## TypeChildren section + +``` +TypeChildren { + NSyms uint32 + Offsets [NSyms+1]uint32 + Data []uint32 // 子类型 LocalSymbol 的连续数组 +} +``` + +记录类型描述符中包含的子类型引用: +- struct 类型 → 各字段的类型 +- 指针类型 → 指向的元素类型 +- slice/array 类型 → 元素类型 +- map 类型 → key 和 value 类型 +- chan 类型 → 元素类型 + +### 双重用途 + +1. **子类型传播**:当父类型被标记为 `usedInIface` 时,沿 TypeChildren 把 `usedInIface` 传播给所有子类型。 +2. **隐式类型识别**:`TypeChildren[sym]` 非空,则 sym 是 composite type。**不需要额外 flag**。 + +对于没有子类型的 named primitive type(如 `type Foo int`),TypeChildren 为空,`usedInIface` 只能通过 `EdgeUseIface` 直接设置,无需通过父类型传播——这是正确的语义。 + +--- + +## MethodInfo section + +``` +MethodInfo { + NSyms uint32 + Offsets [NSyms+1]uint32 + Data []MethodSlot +} + +MethodSlot { // 16 字节定长 + NameRef uint32 // 方法短名(stringTable offset) + MType uint32 // 方法函数类型符号(LocalSymbol) + IFn uint32 // 接口调用入口符号(LocalSymbol) + TFn uint32 // 类型方法入口符号(LocalSymbol) +} +``` + +- 槽位写入顺序与 Go runtime 中该类型的 `abi.Method` 表顺序**严格一致**,槽位 index 即 `abi.Method` 表中的位置。 +- **MethodInfo 中有 entry 的符号即 ConcreteType**(有方法的具体类型),不需要额外 flag。 + +--- + +## InterfaceInfo section + +``` +InterfaceInfo { + NSyms uint32 + Offsets [NSyms+1]uint32 + Data []MethodSig +} + +MethodSig { // 8 字节定长 + NameRef uint32 // 方法短名(stringTable offset) + MType uint32 // 方法函数类型符号(LocalSymbol) +} +``` + +- **InterfaceInfo 中有 entry 的符号即 Interface type**,不需要额外 flag。 +- `EdgeUseIfaceMethod` 中的 `Extra` 字段(方法 index)直接索引此处对应接口的 Data 切片。 + +--- + +## ReflectBitmap section + +``` +ReflectBitmap { + NSyms uint32 + Bitmap [(NSyms+7)/8]uint8 +} +``` + +bit `i` 为 1 表示 symbol `i` 触发了无法静态确定方法名的反射调用(`reflect.Type.Method(index)`、非常量 `MethodByName` 等)。 + +查询: + +```go +func hasReflectMethod(sym LocalSymbol) bool { + return bitmap[sym/8] & (1 << (sym%8)) != 0 +} +``` + +--- + +## 符号语义识别(无 flag 设计) + +DCE 阶段所有符号类型判断**通过 section 存在性隐式表达**,不依赖任何 flag 字段: + +| 判断 | 实现 | +|------|------| +| sym 是 composite type | `TypeChildren.Offsets[i] != TypeChildren.Offsets[i+1]` | +| sym 是 concrete type(有方法) | `MethodInfo.Offsets[i] != MethodInfo.Offsets[i+1]` | +| sym 是 interface type | `InterfaceInfo.Offsets[i] != InterfaceInfo.Offsets[i+1]` | +| sym 使用了反射 | `ReflectBitmap bit i == 1` | + +这个设计对齐了当前 MVP `analyze.go` 中 `TypeChildren`、`MethodSlots`、`InterfaceMethods` 的使用方式,无需引入额外的属性机制。 + +--- + +## mmap 读取 + +```go +type PackageMeta struct { + raw []byte // mmap 映射的字节区域,或 Builder.Build() 产出的字节缓冲 + + // 解析 Header 后缓存的各 section 起始偏移(构造时一次性计算) + strOff uint32 + symOff uint32 + edgesOff uint32 + childrenOff uint32 + methodOff uint32 + ifaceOff uint32 + reflectOff uint32 + + nsyms uint32 // 符号数量,各 section 共享 +} + +// 从文件 mmap +func ReadMeta(path string) (*PackageMeta, error) { + f, _ := os.Open(path) + raw, _ := syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ, syscall.MAP_SHARED) + pm := &PackageMeta{raw: raw, file: f} + pm.parseHeader() + return pm, nil +} + +// 从 Builder 直接构造(Cache MISS 路径) +func (b *Builder) Build() (*PackageMeta, error) { + raw := b.serialize() // Builder 内部 map → wire format + pm := &PackageMeta{raw: raw} + pm.parseHeader() + return pm, nil +} + +// 落盘 +func (pm *PackageMeta) Bytes() []byte { return pm.raw } + +// 查询示例(零分配,直接切 mmap 区域) +func (pm *PackageMeta) TypeChildren(sym LocalSymbol) []LocalSymbol { + section := pm.raw[pm.childrenOff:] + // nsyms := binary.LittleEndian.Uint32(section[:4]) + offsetsBase := section[4:] + start := binary.LittleEndian.Uint32(offsetsBase[sym*4:]) + end := binary.LittleEndian.Uint32(offsetsBase[(sym+1)*4:]) + if start == end { return nil } + dataBase := offsetsBase[(pm.nsyms+1)*4:] + return unsafe.Slice((*LocalSymbol)(unsafe.Pointer(&dataBase[start*4])), end-start) +} +``` + +两条路径(Cache HIT / Cache MISS)产出同一个 `*PackageMeta` 类型,后续 GlobalSummary 合并完全透明。 + +--- + +## GlobalSummary 合并(Lazy Remap) + +合并阶段**只做字符串 intern,不遍历重写边**: + +```go +type GlobalSummary struct { + intern map[string]Symbol // 字符串 → 全局 Symbol ID(O(unique_symbols) 建立) + locToGlb [][]Symbol // [pkgIdx][localSym] → global Symbol + pkgs []*PackageMeta // 各包原始数据保留,不拷贝 + + interfaces []Symbol // InterfaceInfo 有 entry 的全局 Symbol + concreteTypes []Symbol // MethodInfo 有 entry 的全局 Symbol +} + +func NewGlobalSummary(pkgs []*PackageMeta) *GlobalSummary { + g := &GlobalSummary{pkgs: pkgs} + + // 第一步:intern 所有包的字符串表,建立 locToGlb 映射 + // 成本 = O(所有包字符串总数) = O(unique_symbols 数量级) + // 不触碰任何 Edge + for pkgIdx, pkg := range pkgs { + g.locToGlb[pkgIdx] = make([]Symbol, pkg.NSyms()) + for localID := range pkg.NSyms() { + name := pkg.SymbolName(LocalSymbol(localID)) + gid := g.intern.GetOrInsert(name) + g.locToGlb[pkgIdx][localID] = gid + } + } + + // 第二步:收集 interfaces 和 concreteTypes + // 扫 InterfaceInfo 和 MethodInfo 的 key,remap 到全局 Symbol + g.buildTypeIndex() + return g +} + +// 查询时按需翻译(lazy),不预先重写 +func (g *GlobalSummary) Edges(sym Symbol) iter.Seq[Edge] { + return func(yield func(Edge) bool) { + pkgIdx, localSym := g.ownerOf(sym) + pkg := g.pkgs[pkgIdx] + for _, edge := range pkg.Edges(localSym) { + // 翻译 Target 的 LocalSymbol → global Symbol + globalEdge := Edge{ + Target: g.locToGlb[pkgIdx][edge.Target], + Extra: edge.Extra, + Kind: edge.Kind, + } + if !yield(globalEdge) { return } + } + } +} +``` + +合并成本从 **O(total_edges)** 降为 **O(unique_symbols)**,消除前面测量到的 42ms 全局合并开销。 + +--- + +## Cache 集成流程 + +``` +buildPkg(pkg): + metaPath = cachePath(actionID, ".meta") + + if exists(metaPath): + ── Cache HIT ── + aPkg.Meta = ReadMeta(metaPath) // mmap,~微秒级,无反序列化 + else: + ── Cache MISS ── + builder = NewBuilder() + ret = cl.NewPackageEx(..., builder) // cl/ssa 填入类型相关事实 + extractOrdinaryEdges(builder, ret.Module) // 扫 IR 补充普通引用边 + aPkg.Meta = builder.Build() + writeFile(metaPath, aPkg.Meta.Bytes()) // 落盘,下次命中用 + runLLVMPipeline(ret.Module) // 正常 LLVM 编译流程 + + allMetas.append(aPkg.Meta) + +全图阶段: + gs = NewGlobalSummary(allMetas) // O(unique_symbols) intern + liveSlots = deadcode.Analyze(gs) // DCE 算法,~毫秒级 + applyDCE(liveSlots) +``` + +--- + +## 版本与兼容性 + +- `Version` 字段标识格式版本,当前为 1。 +- 读到不支持的 version,直接放弃此缓存,回退到重新编译路径。 +- 格式 version 作为编译器内嵌常量参与 actionID 计算,version 升级自动 invalidate 旧缓存。 + +--- + +## 与 MVP 的对比 + +| 维度 | MVP | 本格式 | +|------|-----|--------| +| 读取方式 | 逐字节反序列化(uvarint) | mmap 零拷贝 | +| 合并方式 | 遍历全部边重写 global ID(O(edges),~42ms) | 只 intern 字符串(O(symbols),~微秒) | +| 反序列化成本 | ~50ms(read + decode) | ~微秒(mmap syscall) | +| 符号属性 | `map[Symbol]struct{}` in memory | 通过 section 存在性隐式表达,零额外存储 | +| TypeChildren | 独立 section + 函数递归展开 | 独立 section + worklist(无 Go 函数递归) | +| 是否类型判断 | `typeSymbols map` | `TypeChildren[i] 非空` | +| ReflectMethod | 独立 section | bitmap(每符号 1 bit) | +| UseIface/UseIfaceMethod/UseNamedMethod | 独立 section | 合并进 Edges,Kind 字段区分 | + +--- + +## 空间估算 + +以中等规模包(1000 符号、5000 边、200 类型、20 接口)为例: + +| Section | 大小 | +|---------|------| +| Header | 40 B | +| stringTable | ~50 KB | +| Symbols | 12 × 1000 = 12 KB | +| Edges offsets | 4 × 1001 = ~4 KB | +| Edges data | 12 × 5000 = 60 KB | +| TypeChildren offsets | 4 × 1001 = ~4 KB | +| TypeChildren data | 4 × ~500 = ~2 KB | +| MethodInfo offsets | 4 × 1001 = ~4 KB | +| MethodInfo data | 16 × 1000 = 16 KB | +| InterfaceInfo offsets | 4 × 1001 = ~4 KB | +| InterfaceInfo data | 8 × 80 = ~1 KB | +| ReflectBitmap | ~125 B | +| **合计** | **~157 KB / 包** | + +100 个包约 ~15 MB,mmap 无压力。 diff --git a/internal/meta/build.go b/internal/meta/build.go new file mode 100644 index 0000000000..2b5446e6bd --- /dev/null +++ b/internal/meta/build.go @@ -0,0 +1,248 @@ +package meta + +import "encoding/binary" + +// Build serializes all accumulated facts into a PackageMeta. +// +// The process is: +// 1. Calculate the byte size of every section. +// 2. Derive each section's start offset. +// 3. Allocate one []byte for the whole file. +// 4. Write header + every section directly into the buffer — no intermediate +// allocations, no copies. +func (b *Builder) Build() (*PackageMeta, error) { + nsyms := uint32(len(b.symNames)) + + // ── 1. calculate section sizes ──────────────────────────────────────────── + + // stringTable is padded to a 4-byte boundary so every following section + // starts 4-byte aligned, enabling zero-copy unsafe access (e.g. TypeChildren). + strSize := align4(uint32(len(b.strData))) + + symSize := 4 + nsyms*12 // nsyms u32 + N×SymbolRecord(12) + + totalEdges := uint32(0) + for _, es := range b.edges { + totalEdges += uint32(len(es)) + } + edgeSize := 4 + (nsyms+1)*4 + totalEdges*12 // nsyms + offsets[N+1] + N×Edge(12) + + totalChildren := uint32(0) + for _, cs := range b.typeChildren { + totalChildren += uint32(len(cs)) + } + childSize := 4 + (nsyms+1)*4 + totalChildren*4 + + totalSlots := uint32(0) + for _, ms := range b.methodInfo { + totalSlots += uint32(len(ms)) + } + methodSize := 4 + (nsyms+1)*4 + totalSlots*20 // N×MethodSlot(20: NameRef(8)+mtype+ifn+tfn) + + totalSigs := uint32(0) + for _, ss := range b.ifaceInfo { + totalSigs += uint32(len(ss)) + } + ifaceSize := 4 + (nsyms+1)*4 + totalSigs*12 // N×MethodSig(12: NameRef(8)+mtype) + + reflSize := 4 + (nsyms+7)/8 // nsyms u32 + bitmap bytes + + // ── 2. calculate section offsets ───────────────────────────────────────── + + var offsets [numSections]uint32 + cur := uint32(headerSize) + offsets[SecStringTable] = cur + cur += strSize + offsets[SecSymbols] = cur + cur += symSize + offsets[SecEdges] = cur + cur += edgeSize + offsets[SecTypeChildren] = cur + cur += childSize + offsets[SecMethodInfo] = cur + cur += methodSize + offsets[SecIfaceInfo] = cur + cur += ifaceSize + offsets[SecReflect] = cur + cur += reflSize + + // ── 3. allocate one buffer ──────────────────────────────────────────────── + + raw := make([]byte, cur) + + // ── 4. write header ─────────────────────────────────────────────────────── + + copy(raw[0:4], Magic) + binary.LittleEndian.PutUint32(raw[4:8], Version) + for i, off := range offsets { + binary.LittleEndian.PutUint32(raw[8+i*4:], off) + } + + // ── 5. write each section directly into raw ─────────────────────────────── + + writeStringTable(raw[offsets[SecStringTable]:], b) + writeSymbols(raw[offsets[SecSymbols]:], b, nsyms) + writeEdges(raw[offsets[SecEdges]:], b, nsyms) + writeTypeChildren(raw[offsets[SecTypeChildren]:], b, nsyms) + writeMethodInfo(raw[offsets[SecMethodInfo]:], b, nsyms) + writeIfaceInfo(raw[offsets[SecIfaceInfo]:], b, nsyms) + writeReflect(raw[offsets[SecReflect]:], b, nsyms) + + return newPackageMeta(raw) +} + +// ── section writers ─────────────────────────────────────────────────────────── +// Each writer receives a slice starting exactly at its section's offset. +// It writes directly into that slice — no allocation, no copy. + +func writeStringTable(dst []byte, b *Builder) { + // dst may be longer than strData (padding); padding bytes stay zero. + copy(dst, b.strData) +} + +// align4 rounds n up to the next multiple of 4. +func align4(n uint32) uint32 { + return (n + 3) &^ 3 +} + +// writeSymbols writes: +// +// nsyms u32 +// [nsyms] { nameOff u32, nameLen u32, _ [4]byte } (12 bytes each) +func writeSymbols(dst []byte, b *Builder, nsyms uint32) { + binary.LittleEndian.PutUint32(dst, nsyms) + const rec = 12 + for i, e := range b.symNames { + base := 4 + i*rec + binary.LittleEndian.PutUint32(dst[base:], e.nameOff) + binary.LittleEndian.PutUint32(dst[base+4:], e.nameLen) + // dst[base+8 : base+12] reserved, already zero + } +} + +// writeCSRHeader writes: +// +// nsyms u32 +// offsets [nsyms+1] u32 +// +// and returns the slice starting at the data area (after the offsets array). +// cur accumulates the running data index as each symbol's entries are counted. +func writeCSROffsets(dst []byte, nsyms uint32, counts []int) []byte { + binary.LittleEndian.PutUint32(dst, nsyms) + offsetBase := dst[4:] + cur := uint32(0) + for i, c := range counts { + binary.LittleEndian.PutUint32(offsetBase[i*4:], cur) + cur += uint32(c) + } + // sentinel + binary.LittleEndian.PutUint32(offsetBase[len(counts)*4:], cur) + // return slice starting at data area + return dst[4+(nsyms+1)*4:] +} + +// writeEdges writes the Edges section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] { target u32, extra u32, kind u8, _ [3]byte } (12 bytes each) +func writeEdges(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.edges { + counts[i] = len(b.edges[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + const rec = 12 + pos := 0 + for _, es := range b.edges { + for _, e := range es { + binary.LittleEndian.PutUint32(data[pos:], e.target) + binary.LittleEndian.PutUint32(data[pos+4:], e.extra) + data[pos+8] = e.kind + // [pos+9 : pos+12] padding, zero + pos += rec + } + } +} + +// writeTypeChildren writes the TypeChildren section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] u32 (LocalSymbol) +func writeTypeChildren(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.typeChildren { + counts[i] = len(b.typeChildren[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + pos := 0 + for _, cs := range b.typeChildren { + for _, child := range cs { + binary.LittleEndian.PutUint32(data[pos:], uint32(child)) + pos += 4 + } + } +} + +// writeMethodInfo writes the MethodInfo section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] { nameOff u32, nameLen u32, mtype u32, ifn u32, tfn u32 } (20 bytes each) +func writeMethodInfo(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.methodInfo { + counts[i] = len(b.methodInfo[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + const rec = 20 + pos := 0 + for _, slots := range b.methodInfo { + for _, slot := range slots { + binary.LittleEndian.PutUint32(data[pos:], slot.name.Off) + binary.LittleEndian.PutUint32(data[pos+4:], slot.name.Len) + binary.LittleEndian.PutUint32(data[pos+8:], slot.mtype) + binary.LittleEndian.PutUint32(data[pos+12:], slot.ifn) + binary.LittleEndian.PutUint32(data[pos+16:], slot.tfn) + pos += rec + } + } +} + +// writeIfaceInfo writes the InterfaceInfo section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] { nameOff u32, nameLen u32, mtype u32 } (12 bytes each) +func writeIfaceInfo(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.ifaceInfo { + counts[i] = len(b.ifaceInfo[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + const rec = 12 + pos := 0 + for _, sigs := range b.ifaceInfo { + for _, sig := range sigs { + binary.LittleEndian.PutUint32(data[pos:], sig.name.Off) + binary.LittleEndian.PutUint32(data[pos+4:], sig.name.Len) + binary.LittleEndian.PutUint32(data[pos+8:], sig.mtype) + pos += rec + } + } +} + +// writeReflect writes the ReflectBitmap section. +// +// nsyms u32 +// bitmap [(nsyms+7)/8] u8 +func writeReflect(dst []byte, b *Builder, nsyms uint32) { + binary.LittleEndian.PutUint32(dst, nsyms) + bm := dst[4:] + for i := LocalSymbol(0); i < LocalSymbol(nsyms); i++ { + if b.reflectBits.has(i) { + bm[i/8] |= 1 << (i % 8) + } + } +} diff --git a/internal/meta/builder.go b/internal/meta/builder.go new file mode 100644 index 0000000000..81cbf73ab6 --- /dev/null +++ b/internal/meta/builder.go @@ -0,0 +1,189 @@ +package meta + +// bitmap is a bit array backed by []uint32; each uint32 holds 32 bits. +// Indexed by LocalSymbol: bit i lives in [i/32] at position i%32. +// The slice length is always kept in sync with the symbol table by grow(), +// so set() and has() never need to check bounds. +type bitmap []uint32 + +// grow ensures the bitmap can hold bit index i. +// Called whenever a new symbol is registered. +func (bm *bitmap) grow(i LocalSymbol) { + need := uint(i)/32 + 1 + for uint(len(*bm)) < need { + *bm = append(*bm, 0) + } +} + +// set sets bit i. grow(i) must have been called beforehand. +func (bm bitmap) set(i LocalSymbol) { + bm[uint(i)/32] |= 1 << (uint(i) % 32) +} + +// has reports whether bit i is set. +func (bm bitmap) has(i LocalSymbol) bool { + return bm[uint(i)/32]&(1<<(uint(i)%32)) != 0 +} + +// Builder accumulates per-package metadata facts and serializes them into +// the binary wire format understood by PackageMeta. +// +// Typical usage: +// +// b := NewBuilder() +// fn := b.DefSym("main.main") +// callee := b.RefSym("fmt.Println") +// b.AddEdge(fn, callee, EdgeOrdinary, 0) +// pm, err := b.Build() +type Builder struct { + // string interning + strData []byte // raw byte stream, all strings concatenated + strMap map[string]uint32 // string → offset in strData + + // symbol table + symNames []symEntry // indexed by LocalSymbol + symMap map[string]LocalSymbol // name → LocalSymbol + + // per-symbol edge lists (source LocalSymbol → edges) + edges [][]bEdge + + // per-symbol TypeChildren lists + typeChildren [][]LocalSymbol + + // per-symbol MethodInfo (only concrete types) + methodInfo [][]bMethodSlot + + // per-symbol InterfaceInfo (only interface types) + ifaceInfo [][]bMethodSig + + // reflect bitmap: bit i set means symbol i triggers conservative reflection + reflectBits bitmap +} + +type symEntry struct { + nameOff uint32 + nameLen uint32 +} + +type bEdge struct { + target uint32 // LocalSymbol or stringTable offset (UseNamedMethod) + extra uint32 + kind uint8 +} + +type bMethodSlot struct { + name NameRef // method short name + mtype uint32 // LocalSymbol + ifn uint32 // LocalSymbol + tfn uint32 // LocalSymbol +} + +type bMethodSig struct { + name NameRef // method short name + mtype uint32 // LocalSymbol +} + +// NewBuilder creates an empty Builder. +func NewBuilder() *Builder { + return &Builder{ + strMap: make(map[string]uint32), + symMap: make(map[string]LocalSymbol), + } +} + +// internStr adds s to the string byte stream (idempotent) and returns its offset. +func (b *Builder) internStr(s string) uint32 { + if off, ok := b.strMap[s]; ok { + return off + } + off := uint32(len(b.strData)) + b.strData = append(b.strData, s...) + b.strMap[s] = off + return off +} + +// internName registers a name string and returns a NameRef. +func (b *Builder) internName(s string) NameRef { + return NameRef{Off: b.internStr(s), Len: uint32(len(s))} +} + +// Sym registers a symbol by name and returns its LocalSymbol. +// Calling Sym with the same name twice returns the same LocalSymbol. +// Whether the symbol is defined in this package or referenced from another +// makes no difference to the metadata format. +func (b *Builder) Sym(name string) LocalSymbol { + return b.sym(name) +} + +func (b *Builder) sym(name string) LocalSymbol { + if id, ok := b.symMap[name]; ok { + return id + } + id := LocalSymbol(len(b.symNames)) + off := b.internStr(name) + b.symNames = append(b.symNames, symEntry{nameOff: off, nameLen: uint32(len(name))}) + b.symMap[name] = id + // grow all per-symbol structures in sync with the symbol table + b.edges = append(b.edges, nil) + b.typeChildren = append(b.typeChildren, nil) + b.methodInfo = append(b.methodInfo, nil) + b.ifaceInfo = append(b.ifaceInfo, nil) + b.reflectBits.grow(id) // ensure bit slot exists before any MarkReflect call + return id +} + +// AddEdge records a directed edge from src to dst with the given kind and extra. +// +// - EdgeOrdinary: dst is a LocalSymbol; extra = 0 +// - EdgeUseIface: dst is a LocalSymbol (type); extra = 0 +// - EdgeUseIfaceMethod: dst is a LocalSymbol (interface); extra = method index +// - EdgeUseNamedMethod: dst is a string (method name); extra = 0 +func (b *Builder) AddEdge(src, dst LocalSymbol, kind uint8, extra uint32) { + b.edges[src] = append(b.edges[src], bEdge{ + target: uint32(dst), + extra: extra, + kind: kind, + }) +} + +// AddNamedMethodEdge records an EdgeUseNamedMethod edge where the target is a +// method name string rather than a LocalSymbol. The name's byte offset is stored +// in target and its length in extra, together forming a NameRef. +func (b *Builder) AddNamedMethodEdge(src LocalSymbol, methodName string) { + ref := b.internName(methodName) + b.edges[src] = append(b.edges[src], bEdge{ + target: ref.Off, + extra: ref.Len, + kind: EdgeUseNamedMethod, + }) +} + +// AddTypeChild records that parent type structurally contains child type. +func (b *Builder) AddTypeChild(parent, child LocalSymbol) { + b.typeChildren[parent] = append(b.typeChildren[parent], child) +} + +// AddMethodSlot records one ABI method slot for a concrete type. +// Slots must be appended in abi.Method table order. +func (b *Builder) AddMethodSlot(typ LocalSymbol, methodName string, mtype, ifn, tfn LocalSymbol) { + b.methodInfo[typ] = append(b.methodInfo[typ], bMethodSlot{ + name: b.internName(methodName), + mtype: uint32(mtype), + ifn: uint32(ifn), + tfn: uint32(tfn), + }) +} + +// AddIfaceMethod records one method in an interface's method set. +// Methods must be appended in declaration order. +func (b *Builder) AddIfaceMethod(iface LocalSymbol, methodName string, mtype LocalSymbol) { + b.ifaceInfo[iface] = append(b.ifaceInfo[iface], bMethodSig{ + name: b.internName(methodName), + mtype: uint32(mtype), + }) +} + +// MarkReflect marks sym as triggering conservative reflection handling. +func (b *Builder) MarkReflect(sym LocalSymbol) { + b.reflectBits.set(sym) +} diff --git a/internal/meta/global.go b/internal/meta/global.go new file mode 100644 index 0000000000..7a960f0d17 --- /dev/null +++ b/internal/meta/global.go @@ -0,0 +1,327 @@ +package meta + +// GlobalSummary is a whole-program metadata view over multiple PackageMetas, +// in one unified symbol/name space. +// +// Merge strategy: +// - Symbols are interned into a global Symbol space; each package's local +// symbols are mapped via locToGlb. Edges and TypeChildren are NOT rewritten +// at merge time — they are translated lazily on query (the bulk of the data, +// and the main cost we avoid up front). +// - MethodInfo / InterfaceInfo / reflect are small, so they are translated +// eagerly at merge time, interning method names into the global Name space. +// - Duplicate symbols (e.g. linkonce type descriptors emitted by several +// packages) are first-wins: the first package that owns facts for a symbol +// becomes its owner; later duplicates are ignored. +type GlobalSummary struct { + pkgs []*PackageMeta + + // symbol space + symIntern map[string]Symbol + symStrings []string // Symbol → text + locToGlb [][]Symbol // [pkgIdx][localSym] → global Symbol + owner []symLoc // global Symbol → owning (pkg, local); pkg<0 if none + + // method-name space (distinct from symbols) + nameIntern map[string]Name + nameStrings []string // Name → text + + // eagerly translated small sections + methodInfo map[Symbol][]GMethodSlot + interfaceInfo map[Symbol][]GMethodSig + reflect map[Symbol]struct{} + + interfaces []Symbol + concreteTypes []Symbol +} + +// Symbol is a whole-program symbol ID in GlobalSummary's unified namespace. +type Symbol uint32 + +// Name is a whole-program method-name ID, in a namespace distinct from Symbol. +type Name uint32 + +// GMethodSlot is a method slot in the global namespace. +type GMethodSlot struct { + Name Name + MType Symbol + IFn Symbol + TFn Symbol +} + +// GMethodSig is an interface method signature in the global namespace. +type GMethodSig struct { + Name Name + MType Symbol +} + +// IfaceMethodDemand is a reachable interface method call: a demand that some +// type's method matching Sig on interface Target be kept. +type IfaceMethodDemand struct { + Target Symbol + Sig GMethodSig +} + +// symLoc identifies a (package, local symbol) pair. pkg < 0 means "no owner". +type symLoc struct { + pkg int32 + local LocalSymbol +} + +// NewGlobalSummary merges package-local metadata into a whole-program view. +func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { + g := &GlobalSummary{ + pkgs: pkgs, + symIntern: make(map[string]Symbol), + nameIntern: make(map[string]Name), + locToGlb: make([][]Symbol, len(pkgs)), + methodInfo: make(map[Symbol][]GMethodSlot), + interfaceInfo: make(map[Symbol][]GMethodSig), + reflect: make(map[Symbol]struct{}), + } + + // Phase 1: intern symbols, build locToGlb and owner (used for lazy + // edges/children translation). Touches no edges. + for pi, pm := range pkgs { + if pm == nil { + continue + } + n := pm.NSyms() + tab := make([]Symbol, n) + for li := LocalSymbol(0); li < LocalSymbol(n); li++ { + gs := g.internSymbol(pm.SymbolName(li)) + tab[li] = gs + if g.owner[gs].pkg < 0 && hasFacts(pm, li) { + g.owner[gs] = symLoc{pkg: int32(pi), local: li} + } + } + g.locToGlb[pi] = tab + } + + // Phase 2: eagerly translate small sections (method/iface/reflect), + // interning method names into the global Name space. First-wins on dups. + for pi, pm := range pkgs { + if pm == nil { + continue + } + tab := g.locToGlb[pi] + n := pm.NSyms() + for li := LocalSymbol(0); li < LocalSymbol(n); li++ { + gs := tab[li] + if pm.IsConcreteType(li) { + if _, done := g.methodInfo[gs]; !done { + g.methodInfo[gs] = g.translateSlots(tab, pm, li) + g.concreteTypes = append(g.concreteTypes, gs) + } + } + if pm.IsInterface(li) { + if _, done := g.interfaceInfo[gs]; !done { + g.interfaceInfo[gs] = g.translateSigs(tab, pm, li) + g.interfaces = append(g.interfaces, gs) + } + } + if pm.HasReflect(li) { + g.reflect[gs] = struct{}{} + } + } + } + return g, nil +} + +// hasFacts reports whether li carries any facts in pm (i.e. is defined here, +// not merely referenced). Used to pick the owning package for lazy queries. +func hasFacts(pm *PackageMeta, li LocalSymbol) bool { + return pm.HasEdges(li) || + pm.IsCompositeType(li) || + pm.IsConcreteType(li) || + pm.IsInterface(li) || + pm.HasReflect(li) +} + +func (g *GlobalSummary) internSymbol(s string) Symbol { + if id, ok := g.symIntern[s]; ok { + return id + } + id := Symbol(len(g.symStrings)) + g.symIntern[s] = id + g.symStrings = append(g.symStrings, s) + g.owner = append(g.owner, symLoc{pkg: -1}) + return id +} + +func (g *GlobalSummary) internName(s string) Name { + if id, ok := g.nameIntern[s]; ok { + return id + } + id := Name(len(g.nameStrings)) + g.nameIntern[s] = id + g.nameStrings = append(g.nameStrings, s) + return id +} + +func (g *GlobalSummary) translateSlots(tab []Symbol, pm *PackageMeta, li LocalSymbol) []GMethodSlot { + local := pm.MethodSlots(li) + out := make([]GMethodSlot, len(local)) + for i, s := range local { + out[i] = GMethodSlot{ + Name: g.internName(pm.NameString(s.Name)), + MType: tab[s.MType], + IFn: tab[s.IFn], + TFn: tab[s.TFn], + } + } + return out +} + +func (g *GlobalSummary) translateSigs(tab []Symbol, pm *PackageMeta, li LocalSymbol) []GMethodSig { + local := pm.IfaceMethods(li) + out := make([]GMethodSig, len(local)) + for i, s := range local { + out[i] = GMethodSig{ + Name: g.internName(pm.NameString(s.Name)), + MType: tab[s.MType], + } + } + return out +} + +// ── symbol / name identity ──────────────────────────────────────────────────── + +// LookupSymbol returns the global Symbol for a module-level symbol name. +func (g *GlobalSummary) LookupSymbol(name string) (Symbol, bool) { + id, ok := g.symIntern[name] + return id, ok +} + +// SymbolName returns the text of a global Symbol. +func (g *GlobalSummary) SymbolName(sym Symbol) string { + if int(sym) < len(g.symStrings) { + return g.symStrings[sym] + } + return "" +} + +// Name returns the text of a global Name. +func (g *GlobalSummary) Name(n Name) string { + if int(n) < len(g.nameStrings) { + return g.nameStrings[n] + } + return "" +} + +// ── enumeration ─────────────────────────────────────────────────────────────── + +// Interfaces returns all interface type symbols. +func (g *GlobalSummary) Interfaces() []Symbol { return g.interfaces } + +// ConcreteTypes returns all concrete type symbols with method slots. +func (g *GlobalSummary) ConcreteTypes() []Symbol { return g.concreteTypes } + +// ── eager small sections ────────────────────────────────────────────────────── + +// MethodSlots returns the ABI method slots for concrete type typ. Read-only. +func (g *GlobalSummary) MethodSlots(typ Symbol) []GMethodSlot { return g.methodInfo[typ] } + +// InterfaceMethods returns the method set for interface iface. Read-only. +func (g *GlobalSummary) InterfaceMethods(iface Symbol) []GMethodSig { return g.interfaceInfo[iface] } + +// HasReflectMethod reports whether sym triggers conservative reflection handling. +func (g *GlobalSummary) HasReflectMethod(sym Symbol) bool { + _, ok := g.reflect[sym] + return ok +} + +// ── lazy edge queries ───────────────────────────────────────────────────────── + +// ownerEdges returns the owning package, its locToGlb table, and the raw local +// edges for sym, or nil if sym has no owner. +func (g *GlobalSummary) ownerEdges(sym Symbol) (*PackageMeta, []Symbol, []Edge) { + if int(sym) >= len(g.owner) { + return nil, nil, nil + } + loc := g.owner[sym] + if loc.pkg < 0 { + return nil, nil, nil + } + pm := g.pkgs[loc.pkg] + return pm, g.locToGlb[loc.pkg], pm.Edges(loc.local) +} + +// OrdinaryEdges returns plain reachability targets from sym (global Symbols). +func (g *GlobalSummary) OrdinaryEdges(sym Symbol) []Symbol { + _, tab, edges := g.ownerEdges(sym) + var out []Symbol + for _, e := range edges { + if e.Kind == EdgeOrdinary { + out = append(out, tab[e.Target]) + } + } + return out +} + +// UseIface returns concrete types converted to interfaces by sym. +func (g *GlobalSummary) UseIface(sym Symbol) []Symbol { + _, tab, edges := g.ownerEdges(sym) + var out []Symbol + for _, e := range edges { + if e.Kind == EdgeUseIface { + out = append(out, tab[e.Target]) + } + } + return out +} + +// UseIfaceMethod returns interface method demands emitted by sym. +func (g *GlobalSummary) UseIfaceMethod(sym Symbol) []IfaceMethodDemand { + _, tab, edges := g.ownerEdges(sym) + var out []IfaceMethodDemand + for _, e := range edges { + if e.Kind != EdgeUseIfaceMethod { + continue + } + iface := tab[e.Target] + sigs := g.interfaceInfo[iface] + if int(e.Extra) < len(sigs) { + out = append(out, IfaceMethodDemand{Target: iface, Sig: sigs[e.Extra]}) + } + } + return out +} + +// UseNamedMethod returns constant MethodByName names referenced by sym. +func (g *GlobalSummary) UseNamedMethod(sym Symbol) []Name { + pm, _, edges := g.ownerEdges(sym) + if pm == nil { + return nil + } + var out []Name + for _, e := range edges { + if e.Kind == EdgeUseNamedMethod { + name := pm.NameString(NameRef{Off: e.Target, Len: e.Extra}) + out = append(out, g.internName(name)) + } + } + return out +} + +// TypeChildren returns child type symbols for typ (global Symbols). +func (g *GlobalSummary) TypeChildren(typ Symbol) []Symbol { + if int(typ) >= len(g.owner) { + return nil + } + loc := g.owner[typ] + if loc.pkg < 0 { + return nil + } + pm := g.pkgs[loc.pkg] + tab := g.locToGlb[loc.pkg] + local := pm.TypeChildren(loc.local) + if len(local) == 0 { + return nil + } + out := make([]Symbol, len(local)) + for i, c := range local { + out[i] = tab[c] + } + return out +} diff --git a/internal/meta/global_test.go b/internal/meta/global_test.go new file mode 100644 index 0000000000..58c230f4c3 --- /dev/null +++ b/internal/meta/global_test.go @@ -0,0 +1,173 @@ +package meta_test + +import ( + "testing" + + "github.com/goplus/llgo/internal/meta" +) + +// buildPkgMain builds a "main" package that references a symbol from "runtime" +// and converts a type to an interface defined locally. +func buildPkgMain(t *testing.T) *meta.PackageMeta { + t.Helper() + b := meta.NewBuilder() + + main := b.Sym("main.main") + allocZ := b.Sym("runtime.AllocZ") // defined in runtime, referenced here + myType := b.Sym("*main.Stringer") // defined here + reader := b.Sym("main.Reader") // interface defined here + readT := b.Sym("_llgo_func$Read") + + // main calls runtime.AllocZ, converts *Stringer to Reader, calls Reader.Read + b.AddEdge(main, allocZ, meta.EdgeOrdinary, 0) + b.AddEdge(main, myType, meta.EdgeUseIface, 0) + b.AddEdge(main, reader, meta.EdgeUseIfaceMethod, 0) // Reader.Read = index 0 + + // Reader interface: { Read } + b.AddIfaceMethod(reader, "Read", readT) + + // *Stringer concrete type: slot 0 = Read + rifn := b.Sym("(*Stringer).Read$ifn") + rtfn := b.Sym("(*Stringer).Read$tfn") + b.AddMethodSlot(myType, "Read", readT, rifn, rtfn) + + pm, err := b.Build() + if err != nil { + t.Fatalf("build main: %v", err) + } + return pm +} + +// buildPkgRuntime builds a "runtime" package that defines AllocZ. +func buildPkgRuntime(t *testing.T) *meta.PackageMeta { + t.Helper() + b := meta.NewBuilder() + + allocZ := b.Sym("runtime.AllocZ") // defined here, with a body edge + mallocgc := b.Sym("runtime.mallocgc") + b.AddEdge(allocZ, mallocgc, meta.EdgeOrdinary, 0) + + pm, err := b.Build() + if err != nil { + t.Fatalf("build runtime: %v", err) + } + return pm +} + +func TestGlobalSummaryMerge(t *testing.T) { + mainPkg := buildPkgMain(t) + rtPkg := buildPkgRuntime(t) + defer mainPkg.Close() + defer rtPkg.Close() + + g, err := meta.NewGlobalSummary([]*meta.PackageMeta{mainPkg, rtPkg}) + if err != nil { + t.Fatalf("NewGlobalSummary: %v", err) + } + + sym := func(name string) meta.Symbol { + s, ok := g.LookupSymbol(name) + if !ok { + t.Fatalf("LookupSymbol(%q) not found", name) + } + return s + } + + main := sym("main.main") + allocZ := sym("runtime.AllocZ") + mallocgc := sym("runtime.mallocgc") + myType := sym("*main.Stringer") + reader := sym("main.Reader") + + // ── lazy OrdinaryEdges: main → runtime.AllocZ (cross-package) ────────────── + mainEdges := g.OrdinaryEdges(main) + if len(mainEdges) != 1 || mainEdges[0] != allocZ { + t.Errorf("OrdinaryEdges(main) = %v, want [runtime.AllocZ=%d]", mainEdges, allocZ) + } + + // ── allocZ's edges come from the runtime package (owner) ─────────────────── + azEdges := g.OrdinaryEdges(allocZ) + if len(azEdges) != 1 || azEdges[0] != mallocgc { + t.Errorf("OrdinaryEdges(allocZ) = %v, want [runtime.mallocgc=%d]", azEdges, mallocgc) + } + + // ── UseIface: main converts *Stringer ────────────────────────────────────── + ui := g.UseIface(main) + if len(ui) != 1 || ui[0] != myType { + t.Errorf("UseIface(main) = %v, want [*main.Stringer=%d]", ui, myType) + } + + // ── UseIfaceMethod: main demands Reader.Read ─────────────────────────────── + demands := g.UseIfaceMethod(main) + if len(demands) != 1 { + t.Fatalf("UseIfaceMethod(main): got %d, want 1", len(demands)) + } + if demands[0].Target != reader { + t.Errorf("demand.Target = %d, want reader=%d", demands[0].Target, reader) + } + if g.Name(demands[0].Sig.Name) != "Read" { + t.Errorf("demand.Sig.Name = %q, want \"Read\"", g.Name(demands[0].Sig.Name)) + } + + // ── MethodSlots: *Stringer has Read, name interned globally ──────────────── + slots := g.MethodSlots(myType) + if len(slots) != 1 { + t.Fatalf("MethodSlots(myType): got %d, want 1", len(slots)) + } + if g.Name(slots[0].Name) != "Read" { + t.Errorf("slot name = %q, want \"Read\"", g.Name(slots[0].Name)) + } + // the method name "Read" must intern to the SAME global Name in both the + // interface sig and the concrete slot, so DCE can match them. + if slots[0].Name != demands[0].Sig.Name { + t.Errorf("method name not unified: slot=%d demand=%d", slots[0].Name, demands[0].Sig.Name) + } + + // ── enumeration ──────────────────────────────────────────────────────────── + if len(g.Interfaces()) != 1 || g.Interfaces()[0] != reader { + t.Errorf("Interfaces() = %v, want [reader=%d]", g.Interfaces(), reader) + } + if len(g.ConcreteTypes()) != 1 || g.ConcreteTypes()[0] != myType { + t.Errorf("ConcreteTypes() = %v, want [myType=%d]", g.ConcreteTypes(), myType) + } +} + +// TestGlobalSummaryLinkonce verifies first-wins for a symbol defined (with +// facts) in two packages — a linkonce type descriptor. +func TestGlobalSummaryLinkonce(t *testing.T) { + build := func() *meta.PackageMeta { + b := meta.NewBuilder() + typ := b.Sym("*shared.Foo") + child := b.Sym("shared.Bar") + b.AddTypeChild(typ, child) + mt := b.Sym("_llgo_func$M") + b.AddMethodSlot(typ, "M", mt, b.Sym("ifn"), b.Sym("tfn")) + pm, err := b.Build() + if err != nil { + t.Fatal(err) + } + return pm + } + a, bp := build(), build() + defer a.Close() + defer bp.Close() + + g, err := meta.NewGlobalSummary([]*meta.PackageMeta{a, bp}) + if err != nil { + t.Fatalf("NewGlobalSummary: %v", err) + } + + foo, _ := g.LookupSymbol("*shared.Foo") + + // only one MethodInfo entry survives (first-wins), no duplicate concrete type + if got := len(g.ConcreteTypes()); got != 1 { + t.Errorf("ConcreteTypes() len = %d, want 1 (first-wins)", got) + } + if got := len(g.MethodSlots(foo)); got != 1 { + t.Errorf("MethodSlots(foo) len = %d, want 1", got) + } + // TypeChildren resolves through the owner + if got := len(g.TypeChildren(foo)); got != 1 { + t.Errorf("TypeChildren(foo) len = %d, want 1", got) + } +} diff --git a/internal/meta/meta.go b/internal/meta/meta.go new file mode 100644 index 0000000000..74bc37c047 --- /dev/null +++ b/internal/meta/meta.go @@ -0,0 +1,283 @@ +package meta + +import ( + "encoding/binary" + "fmt" + "os" + "syscall" + "unsafe" +) + +// PackageMeta is a zero-copy view over a .meta file byte slice. +// The underlying bytes may come from an mmap'd file or from Builder.Build(). +// All query methods read directly from the byte slice with no allocation. +type PackageMeta struct { + raw []byte + mmap bool // true → must Munmap on Close + + nsyms uint32 + + // cached section start offsets (parsed once from header) + strOff uint32 + symOff uint32 + edgeOff uint32 + childOff uint32 + methodOff uint32 + ifaceOff uint32 + reflOff uint32 +} + +// Edge is a decoded edge record returned by query methods. Its in-memory layout +// (Target@0, Extra@4, Kind@8, size 12) must match the on-disk wire layout exactly +// so Edges can reinterpret the mmap bytes as []Edge with no copy. +type Edge struct { + Target uint32 // LocalSymbol or stringTable offset (UseNamedMethod) + Extra uint32 + Kind uint8 +} + +// Compile-time assertion: Edge must be exactly 12 bytes. If either const goes +// negative the build fails, pinning the wire/struct layout match. +const ( + _ = uint(unsafe.Sizeof(Edge{}) - 12) + _ = uint(12 - unsafe.Sizeof(Edge{})) +) + +// MethodSlot is a decoded method slot record. Its layout (NameRef@0..8, +// MType@8, IFn@12, TFn@16, size 20) must match the on-disk wire layout for +// zero-copy reads. +type MethodSlot struct { + Name NameRef // method short name + MType LocalSymbol + IFn LocalSymbol + TFn LocalSymbol +} + +// MethodSig is a decoded interface method signature. Layout: NameRef@0..8, +// MType@8, size 12 — must match the on-disk wire layout for zero-copy reads. +type MethodSig struct { + Name NameRef // method short name + MType LocalSymbol +} + +// Compile-time assertions pinning the wire/struct layout for zero-copy reads. +// If a struct's size drifts, one of these uint consts goes negative and the +// build fails. +const ( + _ = uint(unsafe.Sizeof(MethodSlot{}) - 20) + _ = uint(20 - unsafe.Sizeof(MethodSlot{})) + _ = uint(unsafe.Sizeof(MethodSig{}) - 12) + _ = uint(12 - unsafe.Sizeof(MethodSig{})) +) + +// ReadMeta opens path, mmaps it, and returns a PackageMeta view. +// Call Close when done to release the mapping. +func ReadMeta(path string) (*PackageMeta, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return nil, err + } + size := int(fi.Size()) + if size < headerSize { + return nil, fmt.Errorf("meta: file too small: %s", path) + } + + raw, err := syscall.Mmap(int(f.Fd()), 0, size, syscall.PROT_READ, syscall.MAP_SHARED) + if err != nil { + return nil, fmt.Errorf("meta: mmap %s: %w", path, err) + } + + pm, err := newPackageMeta(raw) + if err != nil { + _ = syscall.Munmap(raw) + return nil, err + } + pm.mmap = true + return pm, nil +} + +// NewPackageMetaFromBytes wraps an in-memory byte slice as a PackageMeta. +// The slice must remain valid for the lifetime of the returned PackageMeta. +func NewPackageMetaFromBytes(raw []byte) (*PackageMeta, error) { + return newPackageMeta(raw) +} + +// Bytes returns the underlying raw byte slice (for writing to disk). +func (pm *PackageMeta) Bytes() []byte { return pm.raw } + +// Close releases the mmap mapping if one was used. +func (pm *PackageMeta) Close() error { + if pm.mmap && pm.raw != nil { + err := syscall.Munmap(pm.raw) + pm.raw = nil + return err + } + return nil +} + +// NSyms returns the number of symbols in this package. +func (pm *PackageMeta) NSyms() uint32 { return pm.nsyms } + +// SymbolName returns the name of sym by reading directly from the string table. +func (pm *PackageMeta) SymbolName(sym LocalSymbol) string { + if uint32(sym) >= pm.nsyms { + return "" + } + const recSize = 12 + base := pm.symOff + 4 + uint32(sym)*recSize + nameOff := binary.LittleEndian.Uint32(pm.raw[base+0:]) + nameLen := binary.LittleEndian.Uint32(pm.raw[base+4:]) + return string(pm.raw[pm.strOff+nameOff : pm.strOff+nameOff+nameLen]) +} + +// NameString returns the string referenced by a NameRef. +func (pm *PackageMeta) NameString(ref NameRef) string { + return string(pm.raw[pm.strOff+ref.Off : pm.strOff+ref.Off+ref.Len]) +} + +// Edges returns all edges from sym as a zero-copy view into the mmap region: +// the on-disk 12-byte edge records are reinterpreted directly as []Edge. The +// result is only valid for the lifetime of pm and must not be mutated. Assumes +// a little-endian host and 4-byte aligned data (guaranteed by stringTable padding). +func (pm *PackageMeta) Edges(sym LocalSymbol) []Edge { + if uint32(sym) >= pm.nsyms { + return nil + } + const recSize = 12 + start, end := pm.csrRange(pm.edgeOff, sym) + if start == end { + return nil + } + dataBase := pm.edgeOff + 4 + (pm.nsyms+1)*4 + p := (*Edge)(unsafe.Pointer(&pm.raw[dataBase+start*recSize])) + return unsafe.Slice(p, end-start) +} + +// TypeChildren returns the child type LocalSymbols for sym. +// +// The returned slice is a zero-copy view directly into the mmap region: the +// on-disk uint32 array is reinterpreted as []LocalSymbol with no allocation. +// It is only valid for the lifetime of pm (i.e. until Close) and must not be +// mutated. This assumes a little-endian host (matching the wire format) and a +// 4-byte aligned data section (guaranteed by stringTable padding). +func (pm *PackageMeta) TypeChildren(sym LocalSymbol) []LocalSymbol { + if uint32(sym) >= pm.nsyms { + return nil + } + start, end := pm.csrRange(pm.childOff, sym) + if start == end { + return nil + } + dataBase := pm.childOff + 4 + (pm.nsyms+1)*4 + p := (*LocalSymbol)(unsafe.Pointer(&pm.raw[dataBase+start*4])) + return unsafe.Slice(p, end-start) +} + +// MethodSlots returns the ABI method slots for concrete type sym as a zero-copy +// view into the mmap region. Valid only for the lifetime of pm; do not mutate. +func (pm *PackageMeta) MethodSlots(sym LocalSymbol) []MethodSlot { + if uint32(sym) >= pm.nsyms { + return nil + } + const recSize = 20 + start, end := pm.csrRange(pm.methodOff, sym) + if start == end { + return nil + } + dataBase := pm.methodOff + 4 + (pm.nsyms+1)*4 + p := (*MethodSlot)(unsafe.Pointer(&pm.raw[dataBase+start*recSize])) + return unsafe.Slice(p, end-start) +} + +// IfaceMethods returns the method signatures for interface sym as a zero-copy +// view into the mmap region. Valid only for the lifetime of pm; do not mutate. +func (pm *PackageMeta) IfaceMethods(sym LocalSymbol) []MethodSig { + if uint32(sym) >= pm.nsyms { + return nil + } + const recSize = 12 + start, end := pm.csrRange(pm.ifaceOff, sym) + if start == end { + return nil + } + dataBase := pm.ifaceOff + 4 + (pm.nsyms+1)*4 + p := (*MethodSig)(unsafe.Pointer(&pm.raw[dataBase+start*recSize])) + return unsafe.Slice(p, end-start) +} + +// HasReflect reports whether sym triggers conservative reflection handling. +func (pm *PackageMeta) HasReflect(sym LocalSymbol) bool { + if uint32(sym) >= pm.nsyms { + return false + } + bitmapBase := pm.reflOff + 4 + return pm.raw[bitmapBase+uint32(sym)/8]&(1<<(sym%8)) != 0 +} + +// IsCompositeType reports whether sym has TypeChildren (i.e. is a composite type). +func (pm *PackageMeta) IsCompositeType(sym LocalSymbol) bool { + s, e := pm.csrRange(pm.childOff, sym) + return s != e +} + +// HasEdges reports whether sym has any outgoing edges. +func (pm *PackageMeta) HasEdges(sym LocalSymbol) bool { + s, e := pm.csrRange(pm.edgeOff, sym) + return s != e +} + +// IsConcreteType reports whether sym has MethodSlots (i.e. is a concrete type with methods). +func (pm *PackageMeta) IsConcreteType(sym LocalSymbol) bool { + s, e := pm.csrRange(pm.methodOff, sym) + return s != e +} + +// IsInterface reports whether sym has interface methods (i.e. is an interface type). +func (pm *PackageMeta) IsInterface(sym LocalSymbol) bool { + s, e := pm.csrRange(pm.ifaceOff, sym) + return s != e +} + +// ── internal helpers ────────────────────────────────────────────────────────── + +// newPackageMeta parses the header of raw and returns a PackageMeta. +func newPackageMeta(raw []byte) (*PackageMeta, error) { + if len(raw) < headerSize { + return nil, fmt.Errorf("meta: raw too small (%d bytes)", len(raw)) + } + if string(raw[0:4]) != Magic { + return nil, fmt.Errorf("meta: bad magic %q", raw[0:4]) + } + ver := binary.LittleEndian.Uint32(raw[4:8]) + if ver != Version { + return nil, fmt.Errorf("meta: unsupported version %d", ver) + } + + pm := &PackageMeta{raw: raw} + pm.strOff = binary.LittleEndian.Uint32(raw[8+SecStringTable*4:]) + pm.symOff = binary.LittleEndian.Uint32(raw[8+SecSymbols*4:]) + pm.edgeOff = binary.LittleEndian.Uint32(raw[8+SecEdges*4:]) + pm.childOff = binary.LittleEndian.Uint32(raw[8+SecTypeChildren*4:]) + pm.methodOff = binary.LittleEndian.Uint32(raw[8+SecMethodInfo*4:]) + pm.ifaceOff = binary.LittleEndian.Uint32(raw[8+SecIfaceInfo*4:]) + pm.reflOff = binary.LittleEndian.Uint32(raw[8+SecReflect*4:]) + + // read nsyms from Symbols section header + pm.nsyms = binary.LittleEndian.Uint32(raw[pm.symOff:]) + return pm, nil +} + +// csrRange returns [start, end) data indices for sym in the CSR section +// starting at sectionOff. +func (pm *PackageMeta) csrRange(sectionOff uint32, sym LocalSymbol) (start, end uint32) { + offsetsBase := sectionOff + 4 // skip nsyms u32 + start = binary.LittleEndian.Uint32(pm.raw[offsetsBase+uint32(sym)*4:]) + end = binary.LittleEndian.Uint32(pm.raw[offsetsBase+(uint32(sym)+1)*4:]) + return +} diff --git a/internal/meta/meta_test.go b/internal/meta/meta_test.go new file mode 100644 index 0000000000..9771485f8d --- /dev/null +++ b/internal/meta/meta_test.go @@ -0,0 +1,263 @@ +package meta_test + +import ( + "os" + "testing" + "unsafe" + + "github.com/goplus/llgo/internal/meta" +) + +// TestWireLayout verifies the zero-copy structs match their on-disk byte layout: +// correct total size and field offsets. If these drift, unsafe reinterpretation +// of mmap bytes would silently corrupt — so we assert them explicitly. +func TestWireLayout(t *testing.T) { + if got := unsafe.Sizeof(meta.Edge{}); got != 12 { + t.Errorf("sizeof(Edge) = %d, want 12", got) + } + if got := unsafe.Offsetof(meta.Edge{}.Target); got != 0 { + t.Errorf("Edge.Target offset = %d, want 0", got) + } + if got := unsafe.Offsetof(meta.Edge{}.Extra); got != 4 { + t.Errorf("Edge.Extra offset = %d, want 4", got) + } + if got := unsafe.Offsetof(meta.Edge{}.Kind); got != 8 { + t.Errorf("Edge.Kind offset = %d, want 8", got) + } + + if got := unsafe.Sizeof(meta.MethodSlot{}); got != 20 { + t.Errorf("sizeof(MethodSlot) = %d, want 20", got) + } + if got := unsafe.Offsetof(meta.MethodSlot{}.MType); got != 8 { + t.Errorf("MethodSlot.MType offset = %d, want 8", got) + } + if got := unsafe.Offsetof(meta.MethodSlot{}.TFn); got != 16 { + t.Errorf("MethodSlot.TFn offset = %d, want 16", got) + } + + if got := unsafe.Sizeof(meta.MethodSig{}); got != 12 { + t.Errorf("sizeof(MethodSig) = %d, want 12", got) + } + if got := unsafe.Offsetof(meta.MethodSig{}.MType); got != 8 { + t.Errorf("MethodSig.MType offset = %d, want 8", got) + } +} + +// TestTypeChildrenAlignment uses symbol names of irregular total length so the +// string table is unlikely to land on a 4-byte boundary on its own, verifying +// that stringTable padding keeps the zero-copy TypeChildren view correctly aligned. +func TestTypeChildrenAlignment(t *testing.T) { + for _, pad := range []string{"a", "ab", "abc", "abcd", "abcde"} { + b := meta.NewBuilder() + // a symbol whose name length varies, to shift the string table size + b.Sym("x." + pad) + parent := b.Sym("*pkg.Parent") + c0 := b.Sym("pkg.C0") + c1 := b.Sym("pkg.C1") + c2 := b.Sym("pkg.C2") + b.AddTypeChild(parent, c0) + b.AddTypeChild(parent, c1) + b.AddTypeChild(parent, c2) + + pm, err := b.Build() + if err != nil { + t.Fatalf("pad=%q build: %v", pad, err) + } + got := pm.TypeChildren(parent) + want := []meta.LocalSymbol{c0, c1, c2} + if len(got) != len(want) { + t.Fatalf("pad=%q TypeChildren len = %d, want %d", pad, len(got), len(want)) + } + for i := range want { + if got[i] != want[i] { + t.Errorf("pad=%q child[%d] = %d, want %d", pad, i, got[i], want[i]) + } + } + } +} + +// TestRoundTrip builds a small package summary, serializes it, then reads it +// back and verifies every query returns the expected values. +func TestRoundTrip(t *testing.T) { + b := meta.NewBuilder() + + // symbols + main := b.Sym("main.main") + helper := b.Sym("main.helper") + allocZ := b.Sym("runtime.AllocZ") + myType := b.Sym("*_llgo_main.MyStruct") + myField := b.Sym("_llgo_main.Inner") + myIface := b.Sym("_llgo_iface$Reader") + mtype := b.Sym("_llgo_func$Read") + ifn := b.Sym("(*MyStruct).Read$ifn") + tfn := b.Sym("(*MyStruct).Read$tfn") + + // ordinary edges + b.AddEdge(main, helper, meta.EdgeOrdinary, 0) + b.AddEdge(main, allocZ, meta.EdgeOrdinary, 0) + + // interface conversion + b.AddEdge(main, myType, meta.EdgeUseIface, 0) + + // interface method call: Reader.Read is method index 0 + b.AddEdge(main, myIface, meta.EdgeUseIfaceMethod, 0) + + // named method call + b.AddNamedMethodEdge(helper, "ServeHTTP") + + // TypeChildren: *MyStruct contains Inner + b.AddTypeChild(myType, myField) + + // MethodInfo for *MyStruct: slot 0 = Read + b.AddMethodSlot(myType, "Read", mtype, ifn, tfn) + + // InterfaceInfo for Reader: method 0 = Read + b.AddIfaceMethod(myIface, "Read", mtype) + + // reflect + b.MarkReflect(helper) + + // build + pm, err := b.Build() + if err != nil { + t.Fatalf("Build: %v", err) + } + + // ── verify Symbols ──────────────────────────────────────────────────────── + + checkName := func(sym meta.LocalSymbol, want string) { + t.Helper() + if got := pm.SymbolName(sym); got != want { + t.Errorf("SymbolName(%d) = %q, want %q", sym, got, want) + } + } + checkName(main, "main.main") + checkName(helper, "main.helper") + checkName(allocZ, "runtime.AllocZ") + checkName(myType, "*_llgo_main.MyStruct") + + // ── verify Edges ────────────────────────────────────────────────────────── + + mainEdges := pm.Edges(main) + if len(mainEdges) != 4 { + t.Fatalf("Edges(main): got %d edges, want 4", len(mainEdges)) + } + if e := mainEdges[0]; e.Kind != meta.EdgeOrdinary || meta.LocalSymbol(e.Target) != helper { + t.Errorf("edge[0] = %+v, want {Target:%d Kind:Ordinary}", e, helper) + } + if e := mainEdges[1]; e.Kind != meta.EdgeOrdinary || meta.LocalSymbol(e.Target) != allocZ { + t.Errorf("edge[1] = %+v, want {Target:%d Kind:Ordinary}", e, allocZ) + } + if e := mainEdges[2]; e.Kind != meta.EdgeUseIface || meta.LocalSymbol(e.Target) != myType { + t.Errorf("edge[2] = %+v, want {Target:%d Kind:UseIface}", e, myType) + } + if e := mainEdges[3]; e.Kind != meta.EdgeUseIfaceMethod || meta.LocalSymbol(e.Target) != myIface || e.Extra != 0 { + t.Errorf("edge[3] = %+v, want {Target:%d Kind:UseIfaceMethod Extra:0}", e, myIface) + } + + helperEdges := pm.Edges(helper) + if len(helperEdges) != 1 { + t.Fatalf("Edges(helper): got %d, want 1", len(helperEdges)) + } + if e := helperEdges[0]; e.Kind != meta.EdgeUseNamedMethod { + t.Errorf("helper edge[0].Kind = %d, want UseNamedMethod", e.Kind) + } + // For UseNamedMethod, target=NameRef.Off and extra=NameRef.Len. + gotName := pm.NameString(meta.NameRef{Off: helperEdges[0].Target, Len: helperEdges[0].Extra}) + if gotName != "ServeHTTP" { + t.Errorf("UseNamedMethod target name = %q, want \"ServeHTTP\"", gotName) + } + if got := pm.Edges(allocZ); len(got) != 0 { + t.Errorf("Edges(allocZ): got %d, want 0", len(got)) + } + + // ── verify TypeChildren ─────────────────────────────────────────────────── + + children := pm.TypeChildren(myType) + if len(children) != 1 || children[0] != myField { + t.Errorf("TypeChildren(myType) = %v, want [%d]", children, myField) + } + if pm.TypeChildren(main) != nil { + t.Errorf("TypeChildren(main) should be nil") + } + if !pm.IsCompositeType(myType) { + t.Errorf("IsCompositeType(myType) = false, want true") + } + if pm.IsCompositeType(main) { + t.Errorf("IsCompositeType(main) = true, want false") + } + + // ── verify MethodSlots ──────────────────────────────────────────────────── + + slots := pm.MethodSlots(myType) + if len(slots) != 1 { + t.Fatalf("MethodSlots(myType): got %d, want 1", len(slots)) + } + slot := slots[0] + if pm.NameString(slot.Name) != "Read" { + t.Errorf("slot.Name = %q, want \"Read\"", pm.NameString(slot.Name)) + } + if slot.MType != mtype || slot.IFn != ifn || slot.TFn != tfn { + t.Errorf("slot = %+v, unexpected symbols", slot) + } + if !pm.IsConcreteType(myType) { + t.Errorf("IsConcreteType(myType) = false, want true") + } + + // ── verify IfaceMethods ─────────────────────────────────────────────────── + + sigs := pm.IfaceMethods(myIface) + if len(sigs) != 1 { + t.Fatalf("IfaceMethods(myIface): got %d, want 1", len(sigs)) + } + if pm.NameString(sigs[0].Name) != "Read" { + t.Errorf("iface method name = %q, want \"Read\"", pm.NameString(sigs[0].Name)) + } + if !pm.IsInterface(myIface) { + t.Errorf("IsInterface(myIface) = false, want true") + } + if pm.IsInterface(main) { + t.Errorf("IsInterface(main) = true, want false") + } + + // ── verify ReflectBitmap ────────────────────────────────────────────────── + + if !pm.HasReflect(helper) { + t.Errorf("HasReflect(helper) = false, want true") + } + if pm.HasReflect(main) { + t.Errorf("HasReflect(main) = true, want false") + } +} + +// TestRoundTripFile writes the meta to disk and reads it back via ReadMeta. +func TestRoundTripFile(t *testing.T) { + b := meta.NewBuilder() + fn := b.Sym("pkg.Fn") + dep := b.Sym("runtime.X") + b.AddEdge(fn, dep, meta.EdgeOrdinary, 0) + + pm, err := b.Build() + if err != nil { + t.Fatalf("Build: %v", err) + } + + path := t.TempDir() + "/test.meta" + if err := os.WriteFile(path, pm.Bytes(), 0644); err != nil { + t.Fatalf("write: %v", err) + } + + pm2, err := meta.ReadMeta(path) + if err != nil { + t.Fatalf("ReadMeta: %v", err) + } + defer pm2.Close() + + if got := pm2.SymbolName(fn); got != "pkg.Fn" { + t.Errorf("SymbolName after file round-trip = %q, want \"pkg.Fn\"", got) + } + edges := pm2.Edges(fn) + if len(edges) != 1 || meta.LocalSymbol(edges[0].Target) != dep { + t.Errorf("Edges after file round-trip = %v", edges) + } +} diff --git a/internal/meta/types.go b/internal/meta/types.go new file mode 100644 index 0000000000..e2d81001ca --- /dev/null +++ b/internal/meta/types.go @@ -0,0 +1,51 @@ +// Package meta defines the binary format and in-memory view for LLGo package +// summary cache files (.meta). The format is designed for zero-copy access via +// mmap: the file layout is the memory layout. +package meta + +// LocalSymbol is a package-local symbol ID, equal to its index in the Symbols +// section. Valid within one PackageMeta only; use GlobalSummary for cross-package +// references. +type LocalSymbol uint32 + +// NameRef references a name string by its byte range in the string table. +// It is used for method short names, which are matched by value across packages +// — names are not module-level symbols and live in their own namespace. +type NameRef struct { + Off uint32 + Len uint32 +} + +// Edge kinds used in the Edges section. +const ( + // EdgeOrdinary is a plain symbol reference (call, type use, global var, etc.). + EdgeOrdinary uint8 = 0 + // EdgeUseIface marks that the source converts Target type to an interface. + EdgeUseIface uint8 = 1 + // EdgeUseIfaceMethod marks a call to method Extra of interface Target. + EdgeUseIfaceMethod uint8 = 2 + // EdgeUseNamedMethod marks a constant MethodByName call; Target is a + // stringTable byte offset (not a LocalSymbol). + EdgeUseNamedMethod uint8 = 3 +) + +// Magic is the 4-byte file signature. +const Magic = "LLPM" + +// Version is the current binary format version. +const Version = 1 + +// Section index constants for Header.SectionOffsets. +const ( + SecStringTable = 0 + SecSymbols = 1 + SecEdges = 2 + SecTypeChildren = 3 + SecMethodInfo = 4 + SecIfaceInfo = 5 + SecReflect = 6 + numSections = 7 +) + +// headerSize = magic(4) + version(4) + sectionOffsets(numSections×4) +const headerSize = 4 + 4 + numSections*4 From 35526664d1982a9f8f8426aeae3c19b84ce00d0d Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sat, 27 Jun 2026 17:48:57 +0800 Subject: [PATCH 16/37] feat: migrate DCE pipeline from internal/metadata to internal/meta MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the MVP's uvarint-based metadata format with the new mmap-based zero-copy format (internal/meta). All read paths use unsafe reinterpretation of on-disk bytes; write paths use single-allocation serialization. Key changes: - ssa/cl: switch metaBuilder from *metadata.Builder to *meta.Builder; AddIfaceMethod/AddTypeChild now deduplicate internally - internal/build: wire cache miss/hit paths to meta.ReadMeta/Build/WriteMeta - internal/deadcode: analyze.go uses meta.GlobalSummary with GMethodSlot (flat struct, no nested .Sig) - internal/meta: add zero-copy SymbolName/NameString via unsafe.String, add typeChildrenSet map for O(1) dedup, add FormatMeta for golden tests - cl/_testmeta: update 9 golden files to new deterministic sort order Perf (etcd client demo, 334 packages): summary merge: 114.7ms → ~23ms (5x faster via lazy remap + zero-copy strings) DCE analyze: ~34ms unchanged total: ~148ms → ~52ms (2.8x faster) Co-Authored-By: Claude --- cl/_testmeta/ifaceuse_basic/meta-expect.txt | 4 +- .../interface_anyonmous/meta-expect.txt | 28 +- .../interface_exported_var/meta-expect.txt | 64 +-- cl/_testmeta/interface_named/meta-expect.txt | 22 +- .../interface_unexported/meta-expect.txt | 22 +- .../methodinfo_imported/meta-expect.txt | 100 ++-- cl/_testmeta/reflect_dynamic/meta-expect.txt | 14 +- cl/_testmeta/reflect_named/meta-expect.txt | 104 ++-- .../typechildren_basic/meta-expect.txt | 10 +- cl/cltest/cltest.go | 4 +- cl/compile.go | 6 +- cl/instr.go | 9 +- internal/build/build.go | 30 +- internal/build/cache.go | 15 +- internal/build/cache_test.go | 12 +- internal/build/collect.go | 4 +- internal/build/collect_test.go | 35 +- internal/build/dce_override_test.go | 36 +- internal/build/metadata_edges.go | 20 +- internal/build/metadata_edges_test.go | 41 +- internal/deadcode/analyze.go | 85 ++-- internal/deadcode/analyze_test.go | 472 +++++++++--------- internal/meta/builder.go | 28 +- internal/meta/format.go | 177 +++++++ internal/meta/meta.go | 12 +- ssa/abitype.go | 22 +- ssa/expr.go | 9 +- ssa/interface.go | 30 +- ssa/package.go | 4 +- 29 files changed, 813 insertions(+), 606 deletions(-) create mode 100644 internal/meta/format.go diff --git a/cl/_testmeta/ifaceuse_basic/meta-expect.txt b/cl/_testmeta/ifaceuse_basic/meta-expect.txt index 6f5a9b9355..434b667c9e 100644 --- a/cl/_testmeta/ifaceuse_basic/meta-expect.txt +++ b/cl/_testmeta/ifaceuse_basic/meta-expect.txt @@ -11,14 +11,14 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.init: github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.init$guard github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.main: - github.com/goplus/llgo/runtime/internal/runtime.AllocU _llgo_github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.T github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.sink + github.com/goplus/llgo/runtime/internal/runtime.AllocU [UseIface] github.com/goplus/llgo/cl/_testmeta/ifaceuse_basic.main: diff --git a/cl/_testmeta/interface_anyonmous/meta-expect.txt b/cl/_testmeta/interface_anyonmous/meta-expect.txt index 8dec3da93c..f20d5aaf97 100644 --- a/cl/_testmeta/interface_anyonmous/meta-expect.txt +++ b/cl/_testmeta/interface_anyonmous/meta-expect.txt @@ -8,11 +8,6 @@ _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -[InterfaceInfo] -_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: - M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac - N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac - [OrdinaryEdges] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr @@ -40,30 +35,30 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal *_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo$imethods _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo$imethods: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M: - github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer - github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M -github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N: - github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N: github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.init: github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.init$guard github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.main: - github.com/goplus/llgo/runtime/internal/runtime.AllocU - _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T - github.com/goplus/llgo/runtime/internal/runtime.NewItab + _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.NewItab github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.use: github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData @@ -84,3 +79,8 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.M 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.(*T).N __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_anyonmous.T.N +[InterfaceInfo] +_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: + M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt index 97affb155a..b7237af82b 100644 --- a/cl/_testmeta/interface_exported_var/meta-expect.txt +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -67,28 +67,18 @@ _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_string _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: - _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg - _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 - _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus - _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE - -[InterfaceInfo] -_llgo_encoding/binary.ByteOrder: - PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU - PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg - PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 - String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus - Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc - Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to [OrdinaryEdges] *[]_llgo_uint8: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr []_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr *_llgo_encoding/binary.littleEndian: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_encoding/binary.littleEndian @@ -204,8 +194,8 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: github.com/goplus/llgo/runtime/internal/runtime.strequal _llgo_encoding/binary.littleEndian: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_encoding/binary.littleEndian + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: *_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs$in @@ -281,45 +271,45 @@ _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: _llgo_string _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal *_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods: - _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg - _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 - _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus - _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_string: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal _llgo_uint16: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal16 *_llgo_uint16 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal16 _llgo_uint32: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32 *_llgo_uint32 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32 _llgo_uint64: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 *_llgo_uint64 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 _llgo_uint8: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 *_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init: - github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init$guard encoding/binary.init + github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init$guard github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: - github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref - github.com/goplus/llgo/runtime/internal/runtime.AllocU _llgo_encoding/binary.littleEndian - encoding/binary.LittleEndian - github.com/goplus/llgo/runtime/internal/runtime.Typedmemmove _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw - github.com/goplus/llgo/runtime/internal/runtime.NewItab + encoding/binary.LittleEndian + github.com/goplus/llgo/runtime/internal/runtime.AllocU github.com/goplus/llgo/runtime/internal/runtime.AllocZ + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/runtime/internal/runtime.Typedmemmove [UseIface] github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: @@ -355,3 +345,13 @@ _llgo_encoding/binary.littleEndian: 9 Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc encoding/binary.(*littleEndian).Uint32 __llgo_stub.encoding/binary.littleEndian.Uint32 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 +[InterfaceInfo] +_llgo_encoding/binary.ByteOrder: + PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index 76c246d900..c9133a82ab 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -8,10 +8,6 @@ _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -[InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: - M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac - [OrdinaryEdges] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr @@ -35,26 +31,26 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal *_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: - github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer - github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/interface_named.T.M + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer github.com/goplus/llgo/cl/_testmeta/interface_named.init: github.com/goplus/llgo/cl/_testmeta/interface_named.init$guard github.com/goplus/llgo/cl/_testmeta/interface_named.main: - github.com/goplus/llgo/runtime/internal/runtime.AllocU - _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T - github.com/goplus/llgo/runtime/internal/runtime.NewItab + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 github.com/goplus/llgo/cl/_testmeta/interface_named.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.NewItab github.com/goplus/llgo/cl/_testmeta/interface_named.use: github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData @@ -72,3 +68,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_named.use: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M +[InterfaceInfo] +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: + M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index 834636e0d1..0e8d32cc9c 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -8,10 +8,6 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -[InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: - github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac - [OrdinaryEdges] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr @@ -35,26 +31,26 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: - github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer - github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal *github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.init: github.com/goplus/llgo/cl/_testmeta/interface_unexported.init$guard github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: - github.com/goplus/llgo/runtime/internal/runtime.AllocU - github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T - github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo github.com/goplus/llgo/cl/_testmeta/interface_unexported.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.NewItab github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData @@ -72,3 +68,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m +[InterfaceInfo] +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index 68f003717a..736c5e6778 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -69,8 +69,8 @@ _llgo_uint8 _llgo_bytes.Buffer: []_llgo_uint8 - _llgo_int _llgo_bytes.readOp + _llgo_int _llgo_error: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: @@ -79,12 +79,12 @@ _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: _llgo_int _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: []_llgo_uint8 - _llgo_int _llgo_error + _llgo_int _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: - _llgo_uint8 - _llgo_string _llgo_error + _llgo_string + _llgo_uint8 _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: _llgo_int _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: @@ -92,43 +92,43 @@ _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk: _llgo_func$Z_-7GWzB37LCYRTQLsSYmEihg_hqBK8o_GbT88pqnPY: []_llgo_uint8 _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0: - _llgo_uint8 []_llgo_uint8 _llgo_error + _llgo_uint8 _llgo_func$d4kMA_oCkLwnd1j8nVlv1hwRarEVuCIrDCpnHhDz9UY: - _llgo_int []_llgo_uint8 + _llgo_int _llgo_func$ekGNsrYBSzltfAjxbl6T8H6Yq8j16wzqS3nDj2xxGMU: _llgo_int _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: - _llgo_uint8 _llgo_error + _llgo_uint8 _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: - _llgo_int32 - _llgo_int _llgo_error -_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: _llgo_int + _llgo_int32 +_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: _llgo_bool -_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: - _llgo_string _llgo_int +_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: _llgo_error + _llgo_int + _llgo_string _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: - _llgo_io.Reader - _llgo_int64 _llgo_error + _llgo_int64 + _llgo_io.Reader _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: - _llgo_int32 - _llgo_int _llgo_error + _llgo_int + _llgo_int32 _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: - _llgo_io.Writer - _llgo_int64 _llgo_error + _llgo_int64 + _llgo_io.Writer _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: - _llgo_uint8 _llgo_error + _llgo_uint8 _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_string _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: @@ -138,14 +138,10 @@ _llgo_io.Reader: _llgo_io.Writer: _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk -[InterfaceInfo] -_llgo_io.Reader: - Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk - [OrdinaryEdges] *[]_llgo_uint8: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr []_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr *_llgo_bool: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_bool @@ -312,17 +308,17 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: github.com/goplus/llgo/runtime/internal/runtime.strequal _llgo_bool: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 *_llgo_bool + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 _llgo_bytes.Buffer: *_llgo_bytes.Buffer bytes.struct$8M6lRFZ7Fk2XCr2laNI9Y7uQtk2A8VDBrezMuq2Fkuo$fields _llgo_bytes.readOp: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 *_llgo_bytes.readOp + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 _llgo_error: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal *_llgo_error + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal _llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac @@ -343,8 +339,8 @@ _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$in: []_llgo_uint8 _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$out: - _llgo_int _llgo_error + _llgo_int _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: *_llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$in @@ -352,8 +348,8 @@ _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o: _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$in: _llgo_uint8 _llgo_func$TBlCn7YTQdraI1HMiBWmkrqIGG-8UgD1UVyJy62Z_0o$out: - _llgo_string _llgo_error + _llgo_string _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA: *_llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA _llgo_func$VZ-8VPNF1RaLICwxc1Ghn7BbgyFX3v762OCdx127EkA$in @@ -398,15 +394,15 @@ _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ: *_llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ$out _llgo_func$lukqSsfDYBoIp_R8GMojGkZnrYDqaq2iHn8RkCjW7iQ$out: - _llgo_uint8 _llgo_error + _llgo_uint8 _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y: *_llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y$out _llgo_func$q-bw-_pPYBCXnr1TXIF8sOD4fVVzzIlpHqD-A13AB4Y$out: - _llgo_int32 - _llgo_int _llgo_error + _llgo_int + _llgo_int32 _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: *_llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$in @@ -414,8 +410,8 @@ _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M: _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$in: _llgo_int _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M$out: - _llgo_int _llgo_bool + _llgo_int _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: *_llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$in @@ -423,8 +419,8 @@ _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw: _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$in: _llgo_string _llgo_func$thH5FBpdXzJNnCpSfiLU5ItTntFU6LWp0RJhDm2XJjw$out: - _llgo_int _llgo_error + _llgo_int _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: *_llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8 _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$in @@ -432,8 +428,8 @@ _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8: _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$in: _llgo_io.Reader _llgo_func$uVmBDI0DMcrui3Q9y-g_hbtVN8JckQ18V2wmO5_G7A8$out: - _llgo_int64 _llgo_error + _llgo_int64 _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: *_llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$in @@ -441,8 +437,8 @@ _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw: _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$in: _llgo_int32 _llgo_func$uf8yw1UkUdbDuCneSpNKIq_NThWIEVE7f1IYfJGz_bw$out: - _llgo_int _llgo_error + _llgo_int _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: *_llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$in @@ -450,8 +446,8 @@ _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M: _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$in: _llgo_io.Writer _llgo_func$vSv85k0UY6JWccAc3T-lvdCx9J-4GM-oZC9zGLrxW1M$out: - _llgo_int64 _llgo_error + _llgo_int64 _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: *_llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg$in @@ -470,48 +466,48 @@ _llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods: _llgo_iface$kr1iSWwMezh0B9LdQN0MhEZUNZvBlHPhlst95jAyxE0$imethods: _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal *_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods: _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_int: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 _llgo_int32: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32 *_llgo_int32 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal32 _llgo_int64: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 *_llgo_int64 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 _llgo_io.Reader: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal *_llgo_io.Reader + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods _llgo_io.Writer: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal *_llgo_io.Writer + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal _llgo_iface$kr1iSWwMezh0B9LdQN0MhEZUNZvBlHPhlst95jAyxE0$imethods _llgo_string: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal _llgo_uint8: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 *_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 bytes.struct$8M6lRFZ7Fk2XCr2laNI9Y7uQtk2A8VDBrezMuq2Fkuo$fields: []_llgo_uint8 - _llgo_int _llgo_bytes.readOp + _llgo_int github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.init: - github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.init$guard bytes.init + github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.init$guard io.init github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: - bytes.NewBuffer - _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw *_llgo_bytes.Buffer - github.com/goplus/llgo/runtime/internal/runtime.NewItab + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + bytes.NewBuffer github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + github.com/goplus/llgo/runtime/internal/runtime.NewItab [UseIface] github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: @@ -551,3 +547,7 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: 25 bytes.readSlice _llgo_func$aJkaU3jhXr0Q2QraTe2_TTdupeMMW2MD66UwBxynRM0 bytes.(*Buffer).readSlice __llgo_stub.bytes.(*Buffer).readSlice 26 bytes.tryGrowByReslice _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M bytes.(*Buffer).tryGrowByReslice __llgo_stub.bytes.(*Buffer).tryGrowByReslice +[InterfaceInfo] +_llgo_io.Reader: + Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + diff --git a/cl/_testmeta/reflect_dynamic/meta-expect.txt b/cl/_testmeta/reflect_dynamic/meta-expect.txt index e404868323..7b3a5180a6 100644 --- a/cl/_testmeta/reflect_dynamic/meta-expect.txt +++ b/cl/_testmeta/reflect_dynamic/meta-expect.txt @@ -22,22 +22,22 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M: - github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer - github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.init: github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.init$guard reflect.init github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.main: github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: - github.com/goplus/llgo/runtime/internal/runtime.AllocU _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T - reflect.ValueOf + github.com/goplus/llgo/runtime/internal/runtime.AllocU reflect.Value.MethodByName + reflect.ValueOf [UseIface] github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: @@ -49,6 +49,6 @@ github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use: _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.T.M -[ReflectMethod] -github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use +[Reflect] + github.com/goplus/llgo/cl/_testmeta/reflect_dynamic.use diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index 70d6b07587..187a7c38b4 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -4,46 +4,6 @@ *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T -[InterfaceInfo] -_llgo_reflect.Type: - Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - AssignableTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE - Bits _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - CanSeq _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - CanSeq2 _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - ChanDir _llgo_func$JO3khPIbANSMBmoN6P7ybYAeUBd3Gv6toVUqNeE7qbE - Comparable _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - ConvertibleTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE - Elem _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI - Field _llgo_func$Q3NYrysaKgu1MtMuLQwb-k5QcKGHihnt-tV_NlNJQFA - FieldAlign _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - FieldByIndex _llgo_func$LPPtiM49dEPl48CC3WRhXm3YPnfUJEZE_k8Tx3rMuSk - FieldByName _llgo_func$dEvABJ5r0MMUlf4smWpIDG5dO8AuGklGdNJ1xneL3UM - FieldByNameFunc _llgo_func$xySrXVFC_2LK2oP71R2UryKi6UmdEJUo9k6aQuz4TvI - Implements _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE - In _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY - IsVariadic _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - Key _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI - Kind _llgo_func$w8Mj2LK8G5p7MIiGWR6MYjyXy3L8SVVzYlT1bb6KNXk - Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - Method _llgo_func$FmJJGomlX5kINJGxQdQDCAkD89ySoMslAYFrziWInVc - MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E - Name _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - NumField _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - NumIn _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - NumMethod _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - NumOut _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - Out _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY - OverflowComplex _llgo_func$cGkbH-2LQOLoq64Rqj3WeO56U8al7FfVkf5K1FFbPpE - OverflowFloat _llgo_func$uk7PgUVap9GZdvS8R_mZCDbAbqnAbcNryqybtDogUNI - OverflowInt _llgo_func$odFOIClZoEVGbTP_BEfZxVM5ex3r8Fj1afUEeP_awp8 - OverflowUint _llgo_func$7I97sofX8UqJA96mVIy89KPUfSM_efkrR-mJQ9qaHfk - PkgPath _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - Size _llgo_func$1kITCsyu7hFLMxHLR7kDlvu4SOra_HtrtdFUQH9P13s - String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - reflect.common _llgo_func$w6XuV-1SmW103DbauPseXBpW50HpxXAEsUsGFibl0Uw - reflect.uncommon _llgo_func$iG49bujiXjI2lVflYdE0hPXlCAABL-XKRANSNJEKOio - [OrdinaryEdges] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr @@ -66,32 +26,39 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M: - github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer - github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M -github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m: - github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m: github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer github.com/goplus/llgo/cl/_testmeta/reflect_named.init: github.com/goplus/llgo/cl/_testmeta/reflect_named.init$guard reflect.init github.com/goplus/llgo/cl/_testmeta/reflect_named.main: - github.com/goplus/llgo/runtime/internal/runtime.AllocU _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T - reflect.TypeOf + github.com/goplus/llgo/runtime/internal/runtime.AllocU github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + reflect.TypeOf [UseIface] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T + _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + +[UseNamedMethod] +github.com/goplus/llgo/cl/_testmeta/reflect_named.main: + M + m [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: @@ -101,8 +68,43 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m -[UseNamedMethod] -github.com/goplus/llgo/cl/_testmeta/reflect_named.main: - M - m +[InterfaceInfo] +_llgo_reflect.Type: + Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + AssignableTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Bits _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + CanSeq _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + CanSeq2 _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ChanDir _llgo_func$JO3khPIbANSMBmoN6P7ybYAeUBd3Gv6toVUqNeE7qbE + Comparable _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ConvertibleTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Elem _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Field _llgo_func$Q3NYrysaKgu1MtMuLQwb-k5QcKGHihnt-tV_NlNJQFA + FieldAlign _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + FieldByIndex _llgo_func$LPPtiM49dEPl48CC3WRhXm3YPnfUJEZE_k8Tx3rMuSk + FieldByName _llgo_func$dEvABJ5r0MMUlf4smWpIDG5dO8AuGklGdNJ1xneL3UM + FieldByNameFunc _llgo_func$xySrXVFC_2LK2oP71R2UryKi6UmdEJUo9k6aQuz4TvI + Implements _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + In _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + IsVariadic _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + Key _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Kind _llgo_func$w8Mj2LK8G5p7MIiGWR6MYjyXy3L8SVVzYlT1bb6KNXk + Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Method _llgo_func$FmJJGomlX5kINJGxQdQDCAkD89ySoMslAYFrziWInVc + MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + Name _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + NumField _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumIn _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumMethod _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumOut _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Out _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + OverflowComplex _llgo_func$cGkbH-2LQOLoq64Rqj3WeO56U8al7FfVkf5K1FFbPpE + OverflowFloat _llgo_func$uk7PgUVap9GZdvS8R_mZCDbAbqnAbcNryqybtDogUNI + OverflowInt _llgo_func$odFOIClZoEVGbTP_BEfZxVM5ex3r8Fj1afUEeP_awp8 + OverflowUint _llgo_func$7I97sofX8UqJA96mVIy89KPUfSM_efkrR-mJQ9qaHfk + PkgPath _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + Size _llgo_func$1kITCsyu7hFLMxHLR7kDlvu4SOra_HtrtdFUQH9P13s + String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + reflect.common _llgo_func$w6XuV-1SmW103DbauPseXBpW50HpxXAEsUsGFibl0Uw + reflect.uncommon _llgo_func$iG49bujiXjI2lVflYdE0hPXlCAABL-XKRANSNJEKOio diff --git a/cl/_testmeta/typechildren_basic/meta-expect.txt b/cl/_testmeta/typechildren_basic/meta-expect.txt index dc8229a7eb..80ebfa64ca 100644 --- a/cl/_testmeta/typechildren_basic/meta-expect.txt +++ b/cl/_testmeta/typechildren_basic/meta-expect.txt @@ -33,19 +33,19 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: github.com/goplus/llgo/runtime/internal/runtime.strequal _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner: - github.com/goplus/llgo/runtime/internal/runtime.structequal *_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Inner _llgo_struct$MTAKiWNMPODH0G9-y5PI3BUGLd6hRciSbX1QTpCDOZg$fields -_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: github.com/goplus/llgo/runtime/internal/runtime.structequal +_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer: *_llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer _llgo_struct$VaHvQdd7oPMFznC6BSA9M_OXdmuO77w_Ys1GnWZpjRM$fields + github.com/goplus/llgo/runtime/internal/runtime.structequal _llgo_int: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 _llgo_string: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal _llgo_struct$MTAKiWNMPODH0G9-y5PI3BUGLd6hRciSbX1QTpCDOZg$fields: _llgo_string _llgo_struct$VaHvQdd7oPMFznC6BSA9M_OXdmuO77w_Ys1GnWZpjRM$fields: @@ -54,9 +54,9 @@ _llgo_struct$VaHvQdd7oPMFznC6BSA9M_OXdmuO77w_Ys1GnWZpjRM$fields: github.com/goplus/llgo/cl/_testmeta/typechildren_basic.init: github.com/goplus/llgo/cl/_testmeta/typechildren_basic.init$guard github.com/goplus/llgo/cl/_testmeta/typechildren_basic.main: - github.com/goplus/llgo/runtime/internal/runtime.AllocU _llgo_github.com/goplus/llgo/cl/_testmeta/typechildren_basic.Outer github.com/goplus/llgo/cl/_testmeta/typechildren_basic.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU [UseIface] github.com/goplus/llgo/cl/_testmeta/typechildren_basic.main: diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 48f9c8c660..d7b5ed0860 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -39,7 +39,7 @@ import ( "github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/littest" "github.com/goplus/llgo/internal/llgen" - "github.com/goplus/llgo/internal/metadata" + pkgmeta "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/ssa/ssatest" "github.com/qiniu/x/test" @@ -357,7 +357,7 @@ func withModuleCapture(conf *build.Config, pkgDir string) (*build.Config, *strin return filepath.Dir(file) == pkgDir }) { module = pkg.LPkg.String() - meta = metadata.MetaString(pkg.Meta) + meta = pkgmeta.MetaString(pkg.Meta) } } return &localConf, &module, &meta diff --git a/cl/compile.go b/cl/compile.go index 9d4120394b..513b395fc8 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -31,7 +31,7 @@ import ( "github.com/goplus/llgo/cl/blocks" "github.com/goplus/llgo/internal/goembed" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/typepatch" "golang.org/x/tools/go/ssa" @@ -1639,11 +1639,11 @@ func NewPackageEx(prog llssa.Program, patches Patches, rewrites map[string]strin // NewPackageExWithEmbed compiles a package using pre-loaded go:embed metadata. // // This avoids re-scanning directives when the caller already loaded them. -func NewPackageExWithEmbed(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap goembed.VarMap, metaBuilder *metadata.Builder) (ret llssa.Package, externs []string, err error) { +func NewPackageExWithEmbed(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap goembed.VarMap, metaBuilder *meta.Builder) (ret llssa.Package, externs []string, err error) { return newPackageEx(prog, patches, rewrites, pkg, files, &embedMap, metaBuilder) } -func newPackageEx(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap *goembed.VarMap, metaBuilder *metadata.Builder) (ret llssa.Package, externs []string, err error) { +func newPackageEx(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File, embedMap *goembed.VarMap, metaBuilder *meta.Builder) (ret llssa.Package, externs []string, err error) { pkgProg := pkg.Prog pkgTypes := pkg.Pkg oldTypes := pkgTypes diff --git a/cl/instr.go b/cl/instr.go index 66ef0ea457..2c97e5d791 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -26,7 +26,6 @@ import ( "regexp" "strings" - "github.com/goplus/llgo/internal/metadata" "golang.org/x/tools/go/ssa" llssa "github.com/goplus/llgo/ssa" @@ -101,16 +100,16 @@ func (p *context) markReflectMethodCall(call *ssa.CallCommon) { return } mb := p.pkg.MetaBuilder - owner := mb.Symbol(p.fn.Name()) + owner := mb.Sym(p.fn.Name()) if nameArg == nil { - mb.AddReflectMethod(owner) + mb.MarkReflect(owner) return } if name, ok := constStr(nameArg); ok { - mb.AddUseNamedMethod(owner, []metadata.Name{mb.Name(name)}) + mb.AddNamedMethodEdge(owner, name) return } - mb.AddReflectMethod(owner) + mb.MarkReflect(owner) } // func pystr(string) *py.Object diff --git a/internal/build/build.go b/internal/build/build.go index 1765fa3984..43bf70873f 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -34,6 +34,7 @@ import ( "strings" "sync" "sync/atomic" + "time" "golang.org/x/tools/go/ssa" @@ -49,7 +50,7 @@ import ( "github.com/goplus/llgo/internal/goembed" "github.com/goplus/llgo/internal/header" "github.com/goplus/llgo/internal/lto" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/internal/monitor" "github.com/goplus/llgo/internal/optlevel" @@ -1110,12 +1111,18 @@ func applyDCEOverrides(ctx *context, mainPkg *packages.Package, pkgs []Package, if len(metas) == 0 { return nil } - summary, err := metadata.NewGlobalSummary(metas) + mergeStart := time.Now() + summary, err := meta.NewGlobalSummary(metas) if err != nil { return err } + mergeDur := time.Since(mergeStart) + roots := dceEntryRootCandidates(mainPkg, needRuntime) + analyzeStart := time.Now() liveSlots := deadcode.Analyze(summary, roots) + analyzeDur := time.Since(analyzeStart) + if len(liveSlots) == 0 { return nil } @@ -1124,14 +1131,15 @@ func applyDCEOverrides(ctx *context, mainPkg *packages.Package, pkgs []Package, for _, slots := range liveSlots { liveCount += len(slots) } - fmt.Fprintf(os.Stderr, "[dce] roots=%s live method slots=%d types=%d\n", strings.Join(roots, ","), liveCount, len(liveSlots)) + fmt.Fprintf(os.Stderr, "[dce] packages=%d roots=%s merge=%v analyze=%v live method slots=%d types=%d\n", + len(metas), strings.Join(roots, ","), mergeDur, analyzeDur, liveCount, len(liveSlots)) return dcepass.EmitStrongTypeOverridesDebug(entryPkg.LPkg.Module(), dceSourceModules(pkgs), liveSlots, os.Stderr) } return dcepass.EmitStrongTypeOverrides(entryPkg.LPkg.Module(), dceSourceModules(pkgs), liveSlots) } -func linkedPackageMetas(pkgs []Package) []*metadata.PackageMeta { - metas := make([]*metadata.PackageMeta, 0, len(pkgs)) +func linkedPackageMetas(pkgs []Package) []*meta.PackageMeta { + metas := make([]*meta.PackageMeta, 0, len(pkgs)) for _, pkg := range pkgs { if pkg == nil || pkg.Meta == nil { continue @@ -1356,9 +1364,9 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { return fmt.Errorf("load go:embed directives for %s failed: %w", pkgPath, err) } - var metaBuilder *metadata.Builder + var metaBuilder *meta.Builder if !aPkg.CacheHit { - metaBuilder = metadata.NewBuilder() + metaBuilder = meta.NewBuilder() } ret, externs, err := cl.NewPackageExWithEmbed(ctx.prog, ctx.patches, aPkg.rewriteVars, aPkg.SSA, syntax, embedMap, metaBuilder) check(err) @@ -1366,7 +1374,11 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { aPkg.LPkg = ret if metaBuilder != nil { extractOrdinaryEdges(metaBuilder, ret.Module()) - aPkg.Meta = metaBuilder.Build() + pm, err := metaBuilder.Build() + if err != nil { + return fmt.Errorf("build meta for %s: %w", pkgPath, err) + } + aPkg.Meta = pm } if hook := ctx.buildConf.ModuleHook; hook != nil { hook(aPkg) @@ -1668,7 +1680,7 @@ type aPackage struct { LinkArgs []string ObjFiles []string // object files: .o or .ll (output of compiler, input to archiver) ArchiveFile string // archive file: .a (output of archiver, used for linking) - Meta *metadata.PackageMeta + Meta *meta.PackageMeta rewriteVars map[string]string // Cache related fields diff --git a/internal/build/cache.go b/internal/build/cache.go index 28a3ceb6c8..f6750d3917 100644 --- a/internal/build/cache.go +++ b/internal/build/cache.go @@ -25,7 +25,7 @@ import ( "strings" "github.com/goplus/llgo/internal/env" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" ) const ( @@ -179,7 +179,7 @@ func readManifest(path string) (string, error) { } // writeMeta writes package summary metadata to a file atomically. -func writeMeta(path string, meta *metadata.PackageMeta) error { +func writeMeta(path string, pm *meta.PackageMeta) error { if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return fmt.Errorf("create meta dir: %w", err) } @@ -196,7 +196,7 @@ func writeMeta(path string, meta *metadata.PackageMeta) error { } }() - if err := meta.WriteMeta(tmp); err != nil { + if _, err := tmp.Write(pm.Bytes()); err != nil { return fmt.Errorf("write meta: %w", err) } if err := tmp.Close(); err != nil { @@ -211,13 +211,8 @@ func writeMeta(path string, meta *metadata.PackageMeta) error { } // readMeta reads package summary metadata from a file. -func readMeta(path string) (*metadata.PackageMeta, error) { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - return metadata.ReadMeta(file) +func readMeta(path string) (*meta.PackageMeta, error) { + return meta.ReadMeta(path) } // cacheExists checks if a valid cache entry exists diff --git a/internal/build/cache_test.go b/internal/build/cache_test.go index 1444575eed..5bf7600594 100644 --- a/internal/build/cache_test.go +++ b/internal/build/cache_test.go @@ -19,13 +19,12 @@ package build import ( - "bytes" "os" "path/filepath" "strings" "testing" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" ) func TestSanitizePkgPath(t *testing.T) { @@ -207,12 +206,11 @@ func TestCacheManager_CacheExists(t *testing.T) { func writeTestMetaFile(t *testing.T, path string) { t.Helper() - var buf bytes.Buffer - pm := metadata.NewBuilder().Build() - if err := pm.WriteMeta(&buf); err != nil { - t.Fatalf("WriteMeta: %v", err) + pm, err := meta.NewBuilder().Build() + if err != nil { + t.Fatalf("Build: %v", err) } - if err := os.WriteFile(path, buf.Bytes(), 0o644); err != nil { + if err := os.WriteFile(path, pm.Bytes(), 0o644); err != nil { t.Fatalf("WriteFile %s: %v", path, err) } } diff --git a/internal/build/collect.go b/internal/build/collect.go index 3a991d8b58..339bd8f13a 100644 --- a/internal/build/collect.go +++ b/internal/build/collect.go @@ -27,7 +27,7 @@ import ( "strings" "github.com/goplus/llgo/internal/env" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/packages" gopackages "golang.org/x/tools/go/packages" ) @@ -465,7 +465,7 @@ func (c *context) saveToCache(pkg *aPackage) error { } if pkg.Meta == nil { - pkg.Meta = metadata.NewBuilder().Build() + pkg.Meta, _ = meta.NewBuilder().Build() } if err := writeMeta(paths.Meta, pkg.Meta); err != nil { return err diff --git a/internal/build/collect_test.go b/internal/build/collect_test.go index fdb41d9c49..d308a3599b 100644 --- a/internal/build/collect_test.go +++ b/internal/build/collect_test.go @@ -27,7 +27,7 @@ import ( "testing" "github.com/goplus/llgo/internal/crosscompile" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/packages" gopackages "golang.org/x/tools/go/packages" ) @@ -410,7 +410,7 @@ func TestTryLoadFromCache_ForceRebuild(t *testing.T) { m.pkg.PkgPath = "example.com/cached" return m.Build() }(), - Meta: metadata.NewBuilder().Build(), + Meta: func() *meta.PackageMeta { pm, _ := meta.NewBuilder().Build(); return pm }(), } // Create a temporary .o file @@ -535,7 +535,7 @@ func TestSaveToCache_Success(t *testing.T) { return m.Build() }(), ObjFiles: []string{objFile.Name()}, - Meta: metadata.NewBuilder().Build(), + Meta: func() *meta.PackageMeta { pm, _ := meta.NewBuilder().Build(); return pm }(), } if err := ctx.saveToCache(pkg); err != nil { @@ -572,7 +572,7 @@ func TestSaveToCache_Success(t *testing.T) { t.Errorf("meta should exist: %v", err) } else { defer metaFile.Close() - if _, err := metadata.ReadMeta(metaFile); err != nil { + if _, err := meta.ReadMeta(metaFile.Name()); err != nil { t.Errorf("meta should be readable: %v", err) } } @@ -602,10 +602,10 @@ func TestTryLoadFromCache_LoadsPackageMeta(t *testing.T) { objFile.WriteString("fake object file") objFile.Close() - builder := metadata.NewBuilder() - main := builder.Symbol("pkg.main") - helper := builder.Symbol("pkg.helper") - builder.AddEdge(main, helper) + builder := meta.NewBuilder() + main := builder.Sym("pkg.main") + helper := builder.Sym("pkg.helper") + builder.AddEdge(main, helper, meta.EdgeOrdinary, 0) pkg := &aPackage{ Package: &packages.Package{ @@ -620,8 +620,8 @@ func TestTryLoadFromCache_LoadsPackageMeta(t *testing.T) { return m.Build() }(), ObjFiles: []string{objFile.Name()}, - Meta: builder.Build(), } + pkg.Meta, _ = builder.Build() if err := ctx.saveToCache(pkg); err != nil { t.Fatalf("saveToCache: %v", err) @@ -638,12 +638,19 @@ func TestTryLoadFromCache_LoadsPackageMeta(t *testing.T) { if pkg.Meta == nil { t.Fatal("Meta was not loaded from cache") } - var edges []metadata.Symbol - pkg.Meta.ForEachOrdinaryEdge(func(src metadata.Symbol, dsts []metadata.Symbol) { - if pkg.Meta.SymbolName(src) == "pkg.main" { - edges = append(edges, dsts...) + var mainSym meta.LocalSymbol + for i := meta.LocalSymbol(0); i < meta.LocalSymbol(pkg.Meta.NSyms()); i++ { + if pkg.Meta.SymbolName(i) == "pkg.main" { + mainSym = i + break } - }) + } + var edges []meta.LocalSymbol + for _, e := range pkg.Meta.Edges(mainSym) { + if e.Kind == meta.EdgeOrdinary { + edges = append(edges, meta.LocalSymbol(e.Target)) + } + } if len(edges) != 1 || pkg.Meta.SymbolName(edges[0]) != "pkg.helper" { t.Fatalf("cached metadata edge mismatch: %#v", edges) } diff --git a/internal/build/dce_override_test.go b/internal/build/dce_override_test.go index 3741f25551..5640eda737 100644 --- a/internal/build/dce_override_test.go +++ b/internal/build/dce_override_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/packages" llssa "github.com/goplus/llgo/ssa" "github.com/xgo-dev/llvm" @@ -72,25 +72,23 @@ func TestDCEEntryRootCandidatesSkipsRuntimeWhenNotNeeded(t *testing.T) { } } -func buildDCEMeta() *metadata.PackageMeta { - b := metadata.NewBuilder() - main := b.Symbol("pkg.main") - use := b.Symbol("pkg.use") - typ := b.Symbol("_llgo_pkg.T") - iface := b.Symbol("_llgo_iface$I") - mSig := metadata.MethodSig{Name: b.Name("M"), MType: b.Symbol("_llgo_func$X")} - nSig := metadata.MethodSig{Name: b.Name("N"), MType: b.Symbol("_llgo_func$X")} +func buildDCEMeta() *meta.PackageMeta { + b := meta.NewBuilder() + main := b.Sym("pkg.main") + use := b.Sym("pkg.use") + typ := b.Sym("_llgo_pkg.T") + iface := b.Sym("_llgo_iface$I") + mtype := b.Sym("_llgo_func$X") - b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) - b.AddMethodInfo(typ, []metadata.MethodSlot{ - {Sig: mSig, IFn: b.Symbol("pkg.(*T).M"), TFn: b.Symbol("pkg.T.M")}, - {Sig: nSig, IFn: b.Symbol("pkg.(*T).N"), TFn: b.Symbol("pkg.T.N")}, - }) - b.AddEdge(main, use) - b.AddEdge(main, typ) - b.AddUseIface(main, []metadata.Symbol{typ}) - b.AddUseIfaceMethod(use, []metadata.IfaceMethodDemand{{Target: iface, Sig: mSig}}) - return b.Build() + b.AddIfaceMethod(iface, "M", mtype) + b.AddMethodSlot(typ, "M", mtype, b.Sym("pkg.(*T).M"), b.Sym("pkg.T.M")) + b.AddMethodSlot(typ, "N", mtype, b.Sym("pkg.(*T).N"), b.Sym("pkg.T.N")) + b.AddEdge(main, use, meta.EdgeOrdinary, 0) + b.AddEdge(main, typ, meta.EdgeOrdinary, 0) + b.AddEdge(main, typ, meta.EdgeUseIface, 0) + b.AddEdge(use, iface, meta.EdgeUseIfaceMethod, 0) // M is index 0 in iface + pm, _ := b.Build() + return pm } func addMethodTypeGlobal(t *testing.T, mod llvm.Module, name string) { diff --git a/internal/build/metadata_edges.go b/internal/build/metadata_edges.go index d67d67b0cc..426a47eaa1 100644 --- a/internal/build/metadata_edges.go +++ b/internal/build/metadata_edges.go @@ -3,11 +3,11 @@ package build import ( "strings" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" "github.com/xgo-dev/llvm" ) -func extractOrdinaryEdges(builder *metadata.Builder, mod llvm.Module) { +func extractOrdinaryEdges(builder *meta.Builder, mod llvm.Module) { if builder == nil { return } @@ -38,9 +38,10 @@ func extractOrdinaryEdges(builder *metadata.Builder, mod llvm.Module) { } type ordinaryEdgeCollector struct { - builder *metadata.Builder - src string - seen map[llvm.Value]struct{} + builder *meta.Builder + src string + seen map[llvm.Value]struct{} + addedDst map[string]struct{} // dedup (src, dst) pairs } func (c *ordinaryEdgeCollector) scanGlobalInitializer(v llvm.Value) { @@ -92,7 +93,14 @@ func (c *ordinaryEdgeCollector) add(dst string) { if dst == "" || dst == c.src { return } - c.builder.AddEdge(c.builder.Symbol(c.src), c.builder.Symbol(dst)) + if c.addedDst == nil { + c.addedDst = make(map[string]struct{}) + } + if _, ok := c.addedDst[dst]; ok { + return + } + c.addedDst[dst] = struct{}{} + c.builder.AddEdge(c.builder.Sym(c.src), c.builder.Sym(dst), meta.EdgeOrdinary, 0) } func namedModuleSymbol(v llvm.Value) string { diff --git a/internal/build/metadata_edges_test.go b/internal/build/metadata_edges_test.go index 3cc5f8d80f..130ecf3561 100644 --- a/internal/build/metadata_edges_test.go +++ b/internal/build/metadata_edges_test.go @@ -3,7 +3,7 @@ package build import ( "testing" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" "github.com/xgo-dev/llvm" ) @@ -28,13 +28,13 @@ func TestExtractOrdinaryEdgesFromFunctionAndGlobal(t *testing.T) { global := llvm.AddGlobal(mod, llvm.PointerType(fnTy, 0), "pkg.global") global.SetInitializer(helperFn) - mb := metadata.NewBuilder() + mb := meta.NewBuilder() extractOrdinaryEdges(mb, mod) - pm := mb.Build() + pm, _ := mb.Build() - mainSym := mb.Symbol("pkg.main") - helperSym := mb.Symbol("pkg.helper") - globalSym := mb.Symbol("pkg.global") + mainSym := mb.Sym("pkg.main") + helperSym := mb.Sym("pkg.helper") + globalSym := mb.Sym("pkg.global") if !hasOrdinaryEdge(pm, mainSym, helperSym) { t.Fatalf("missing ordinary edge pkg.main -> pkg.helper") @@ -76,31 +76,24 @@ func TestExtractOrdinaryEdgesSkipsUncommonMethodTable(t *testing.T) { methods, })) - mb := metadata.NewBuilder() + mb := meta.NewBuilder() extractOrdinaryEdges(mb, mod) - pm := mb.Build() + pm, _ := mb.Build() - typeSym := mb.Symbol("_llgo_pkg.T") - if hasOrdinaryEdge(pm, typeSym, mb.Symbol("pkg.(*T).M")) { + typeSym := mb.Sym("_llgo_pkg.T") + if hasOrdinaryEdge(pm, typeSym, mb.Sym("pkg.(*T).M")) { t.Fatalf("method table IFn was recorded as an ordinary edge") } - if hasOrdinaryEdge(pm, typeSym, mb.Symbol("pkg.T.M")) { + if hasOrdinaryEdge(pm, typeSym, mb.Sym("pkg.T.M")) { t.Fatalf("method table TFn was recorded as an ordinary edge") } } -func hasOrdinaryEdge(pm *metadata.PackageMeta, src, dst metadata.Symbol) bool { - found := false - pm.ForEachOrdinaryEdge(func(edgeSrc metadata.Symbol, dsts []metadata.Symbol) { - if edgeSrc != src { - return +func hasOrdinaryEdge(pm *meta.PackageMeta, src, dst meta.LocalSymbol) bool { + for _, e := range pm.Edges(src) { + if e.Kind == meta.EdgeOrdinary && meta.LocalSymbol(e.Target) == dst { + return true } - for _, edgeDst := range dsts { - if edgeDst == dst { - found = true - return - } - } - }) - return found + } + return false } diff --git a/internal/deadcode/analyze.go b/internal/deadcode/analyze.go index 86f2ec5031..40418b53f2 100644 --- a/internal/deadcode/analyze.go +++ b/internal/deadcode/analyze.go @@ -4,53 +4,53 @@ import ( "go/token" "sort" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" ) type ifaceMethodKey struct { - iface metadata.Symbol - sig metadata.MethodSig + iface meta.Symbol + sig meta.GMethodSig } type ifaceMethodName struct { - iface metadata.Symbol - name metadata.Name + iface meta.Symbol + name meta.Name } type methodID struct { - owner metadata.Symbol + owner meta.Symbol slot int } type methodRef struct { - owner metadata.Symbol + owner meta.Symbol slot int - slotInfo metadata.MethodSlot + slotInfo meta.GMethodSlot } type pass struct { - info *metadata.GlobalSummary + info *meta.GlobalSummary methodImplKeys map[methodID][]ifaceMethodKey - typeSymbols map[metadata.Symbol]struct{} + typeSymbols map[meta.Symbol]struct{} - reachable map[metadata.Symbol]struct{} - usedInIface map[metadata.Symbol]struct{} - processedIfaceTy map[metadata.Symbol]struct{} - workQueue []metadata.Symbol + reachable map[meta.Symbol]struct{} + usedInIface map[meta.Symbol]struct{} + processedIfaceTy map[meta.Symbol]struct{} + workQueue []meta.Symbol ifaceMethod map[ifaceMethodKey]struct{} - genericIfaceMethod map[metadata.Name]struct{} + genericIfaceMethod map[meta.Name]struct{} reflectSeen bool markableMethods []methodRef markedMethods map[methodID]struct{} - liveSlots map[metadata.Symbol][]int + liveSlots map[meta.Symbol][]int } // Analyze returns live ABI method slot indexes by concrete type symbol name. -func Analyze(info *metadata.GlobalSummary, rootNames []string) map[string][]int { - roots := make([]metadata.Symbol, 0, len(rootNames)) +func Analyze(info *meta.GlobalSummary, rootNames []string) map[string][]int { + roots := make([]meta.Symbol, 0, len(rootNames)) for _, name := range rootNames { if sym, ok := info.LookupSymbol(name); ok { roots = append(roots, sym) @@ -68,18 +68,18 @@ func Analyze(info *metadata.GlobalSummary, rootNames []string) map[string][]int return out } -func deadcode(info *metadata.GlobalSummary, roots []metadata.Symbol) map[metadata.Symbol][]int { +func deadcode(info *meta.GlobalSummary, roots []meta.Symbol) map[meta.Symbol][]int { d := &pass{ info: info, methodImplKeys: make(map[methodID][]ifaceMethodKey), - typeSymbols: make(map[metadata.Symbol]struct{}), - reachable: make(map[metadata.Symbol]struct{}), - usedInIface: make(map[metadata.Symbol]struct{}), - processedIfaceTy: make(map[metadata.Symbol]struct{}), + typeSymbols: make(map[meta.Symbol]struct{}), + reachable: make(map[meta.Symbol]struct{}), + usedInIface: make(map[meta.Symbol]struct{}), + processedIfaceTy: make(map[meta.Symbol]struct{}), ifaceMethod: make(map[ifaceMethodKey]struct{}), - genericIfaceMethod: make(map[metadata.Name]struct{}), + genericIfaceMethod: make(map[meta.Name]struct{}), markedMethods: make(map[methodID]struct{}), - liveSlots: make(map[metadata.Symbol][]int), + liveSlots: make(map[meta.Symbol][]int), } d.buildMethodImplKeys() @@ -97,11 +97,11 @@ func deadcode(info *metadata.GlobalSummary, roots []metadata.Symbol) map[metadat } func (d *pass) buildMethodImplKeys() { - methodRefs := make(map[metadata.MethodSig][]metadata.Symbol) - ifaceMethodCounts := make(map[metadata.Symbol]int) + methodRefs := make(map[meta.GMethodSig][]meta.Symbol) + ifaceMethodCounts := make(map[meta.Symbol]int) for _, iface := range d.info.Interfaces() { d.typeSymbols[iface] = struct{}{} - seenNames := make(map[metadata.Name]struct{}) + seenNames := make(map[meta.Name]struct{}) for _, sig := range d.info.InterfaceMethods(iface) { methodRefs[sig] = appendSymbolUnique(methodRefs[sig], iface) if _, ok := seenNames[sig.Name]; ok { @@ -115,12 +115,13 @@ func (d *pass) buildMethodImplKeys() { for _, typ := range d.info.ConcreteTypes() { d.typeSymbols[typ] = struct{}{} slots := d.info.MethodSlots(typ) - impls := make(map[metadata.Symbol]int) + impls := make(map[meta.Symbol]int) seen := make(map[ifaceMethodName]struct{}) for _, slot := range slots { - for _, iface := range methodRefs[slot.Sig] { - key := ifaceMethodName{iface: iface, name: slot.Sig.Name} + sig := meta.GMethodSig{Name: slot.Name, MType: slot.MType} + for _, iface := range methodRefs[sig] { + key := ifaceMethodName{iface: iface, name: slot.Name} if _, ok := seen[key]; ok { continue } @@ -131,9 +132,10 @@ func (d *pass) buildMethodImplKeys() { for slotIndex, slot := range slots { id := methodID{owner: typ, slot: slotIndex} - for _, iface := range methodRefs[slot.Sig] { + sig := meta.GMethodSig{Name: slot.Name, MType: slot.MType} + for _, iface := range methodRefs[sig] { if impls[iface] == ifaceMethodCounts[iface] { - key := ifaceMethodKey{iface: iface, sig: slot.Sig} + key := ifaceMethodKey{iface: iface, sig: sig} d.methodImplKeys[id] = append(d.methodImplKeys[id], key) } } @@ -204,11 +206,11 @@ func (d *pass) methodMarkingLoop() bool { } func (d *pass) shouldKeep(method methodRef) bool { - if d.reflectSeen && token.IsExported(d.info.Name(method.slotInfo.Sig.Name)) { + if d.reflectSeen && token.IsExported(d.info.Name(method.slotInfo.Name)) { return true } - if _, ok := d.genericIfaceMethod[method.slotInfo.Sig.Name]; ok { + if _, ok := d.genericIfaceMethod[method.slotInfo.Name]; ok { return true } @@ -229,13 +231,13 @@ func (d *pass) markMethod(method methodRef) bool { d.markedMethods[id] = struct{}{} d.liveSlots[method.owner] = append(d.liveSlots[method.owner], method.slot) - d.markReachable(method.slotInfo.Sig.MType) + d.markReachable(method.slotInfo.MType) d.markReachable(method.slotInfo.IFn) d.markReachable(method.slotInfo.TFn) return true } -func (d *pass) markReachable(sym metadata.Symbol) { +func (d *pass) markReachable(sym meta.Symbol) { if _, ok := d.reachable[sym]; ok { return } @@ -243,7 +245,7 @@ func (d *pass) markReachable(sym metadata.Symbol) { d.workQueue = append(d.workQueue, sym) } -func (d *pass) markUsedInIface(typ metadata.Symbol) { +func (d *pass) markUsedInIface(typ meta.Symbol) { if _, ok := d.usedInIface[typ]; ok { return } @@ -256,21 +258,21 @@ func (d *pass) markUsedInIface(typ metadata.Symbol) { } } -func (d *pass) markTypeUsedInIface(sym metadata.Symbol) { +func (d *pass) markTypeUsedInIface(sym meta.Symbol) { if _, ok := d.typeSymbols[sym]; !ok { return } d.markUsedInIface(sym) } -func (d *pass) popWork() metadata.Symbol { +func (d *pass) popWork() meta.Symbol { sym := d.workQueue[0] copy(d.workQueue, d.workQueue[1:]) d.workQueue = d.workQueue[:len(d.workQueue)-1] return sym } -func appendSymbolUnique(items []metadata.Symbol, item metadata.Symbol) []metadata.Symbol { +func appendSymbolUnique(items []meta.Symbol, item meta.Symbol) []meta.Symbol { for _, existing := range items { if existing == item { return items @@ -278,3 +280,4 @@ func appendSymbolUnique(items []metadata.Symbol, item metadata.Symbol) []metadat } return append(items, item) } + diff --git a/internal/deadcode/analyze_test.go b/internal/deadcode/analyze_test.go index ae00fbe96f..b1495fa968 100644 --- a/internal/deadcode/analyze_test.go +++ b/internal/deadcode/analyze_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" ) func TestAnalyze(t *testing.T) { @@ -18,26 +18,23 @@ func TestAnalyze(t *testing.T) { { name: "keeps interface method implementation", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - use := b.Symbol("pkg.use") - typ := b.Symbol("_llgo_pkg.T") - iface := b.Symbol("_llgo_iface$I") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + use := b.sym("pkg.use") + typ := b.sym("_llgo_pkg.T") + iface := b.sym("_llgo_iface$I") mSig := methodSig(b, "M") nSig := methodSig(b, "N") - b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) - b.AddMethodInfo(typ, []metadata.MethodSlot{ + b.addIfaceEntry(iface, []pkgSig{mSig}) + b.addMethodInfo(typ, []pkgSlot{ methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), methodSlot(b, nSig, "pkg.(*T).N", "pkg.T.N"), }) - b.AddEdge(main, use) - b.AddEdge(main, typ) - b.AddUseIface(main, []metadata.Symbol{typ}) - b.AddUseIfaceMethod(use, []metadata.IfaceMethodDemand{{ - Target: iface, - Sig: mSig, - }}) + b.addEdge(main, use) + b.addEdge(main, typ) + b.addUseIface(main, typ) + b.addUseIfaceMethod(use, iface, mSig) }), want: map[string][]int{"_llgo_pkg.T": {0}}, }, @@ -51,29 +48,26 @@ func TestAnalyze(t *testing.T) { { name: "requires concrete type to implement whole interface", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - useJ := b.Symbol("pkg.useJ") - useI := b.Symbol("pkg.useI") - typ := b.Symbol("_llgo_pkg.T") - iface := b.Symbol("_llgo_iface$I") - compatibleIface := b.Symbol("_llgo_iface$J") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + useJ := b.sym("pkg.useJ") + useI := b.sym("pkg.useI") + typ := b.sym("_llgo_pkg.T") + iface := b.sym("_llgo_iface$I") + compatibleIface := b.sym("_llgo_iface$J") mSig := methodSig(b, "M") nSig := methodSig(b, "N") - b.AddIfaceEntry(iface, []metadata.MethodSig{mSig, nSig}) - b.AddIfaceEntry(compatibleIface, []metadata.MethodSig{mSig}) - b.AddMethodInfo(typ, []metadata.MethodSlot{ + b.addIfaceEntry(iface, []pkgSig{mSig, nSig}) + b.addIfaceEntry(compatibleIface, []pkgSig{mSig}) + b.addMethodInfo(typ, []pkgSlot{ methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), }) - b.AddEdge(main, useJ) - b.AddEdge(main, useI) - b.AddEdge(main, typ) - b.AddUseIface(main, []metadata.Symbol{typ}) - b.AddUseIfaceMethod(useI, []metadata.IfaceMethodDemand{{ - Target: iface, - Sig: mSig, - }}) + b.addEdge(main, useJ) + b.addEdge(main, useI) + b.addEdge(main, typ) + b.addUseIface(main, typ) + b.addUseIfaceMethod(useI, iface, mSig) }), want: map[string][]int{}, }, @@ -85,25 +79,22 @@ func TestAnalyze(t *testing.T) { { name: "requires method type to match", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - use := b.Symbol("pkg.use") - typ := b.Symbol("_llgo_pkg.T") - iface := b.Symbol("_llgo_iface$I") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + use := b.sym("pkg.use") + typ := b.sym("_llgo_pkg.T") + iface := b.sym("_llgo_iface$I") ifaceMSig := methodSigWithType(b, "M", "_llgo_func$int") - typeMSig := methodSigWithType(b, "M", "_llgo_func$string") + typMSig := methodSigWithType(b, "M", "_llgo_func$string") - b.AddIfaceEntry(iface, []metadata.MethodSig{ifaceMSig}) - b.AddMethodInfo(typ, []metadata.MethodSlot{ - methodSlot(b, typeMSig, "pkg.(*T).M", "pkg.T.M"), + b.addIfaceEntry(iface, []pkgSig{ifaceMSig}) + b.addMethodInfo(typ, []pkgSlot{ + methodSlot(b, typMSig, "pkg.(*T).M", "pkg.T.M"), }) - b.AddEdge(main, use) - b.AddEdge(main, typ) - b.AddUseIface(main, []metadata.Symbol{typ}) - b.AddUseIfaceMethod(use, []metadata.IfaceMethodDemand{{ - Target: iface, - Sig: ifaceMSig, - }}) + b.addEdge(main, use) + b.addEdge(main, typ) + b.addUseIface(main, typ) + b.addUseIfaceMethod(use, iface, ifaceMSig) }), want: map[string][]int{}, }, @@ -116,26 +107,23 @@ func TestAnalyze(t *testing.T) { { name: "matches demand recorded before type enters interface semantics", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - useI := b.Symbol("pkg.useI") - makeIface := b.Symbol("pkg.makeIface") - typ := b.Symbol("_llgo_pkg.T") - iface := b.Symbol("_llgo_iface$I") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + useI := b.sym("pkg.useI") + makeIface := b.sym("pkg.makeIface") + typ := b.sym("_llgo_pkg.T") + iface := b.sym("_llgo_iface$I") mSig := methodSig(b, "M") - b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) - b.AddMethodInfo(typ, []metadata.MethodSlot{ + b.addIfaceEntry(iface, []pkgSig{mSig}) + b.addMethodInfo(typ, []pkgSlot{ methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), }) - b.AddEdge(main, useI) - b.AddEdge(main, makeIface) - b.AddEdge(makeIface, typ) - b.AddUseIfaceMethod(useI, []metadata.IfaceMethodDemand{{ - Target: iface, - Sig: mSig, - }}) - b.AddUseIface(makeIface, []metadata.Symbol{typ}) + b.addEdge(main, useI) + b.addEdge(main, makeIface) + b.addEdge(makeIface, typ) + b.addUseIfaceMethod(useI, iface, mSig) + b.addUseIface(makeIface, typ) }), want: map[string][]int{"_llgo_pkg.T": {0}}, }, @@ -149,30 +137,27 @@ func TestAnalyze(t *testing.T) { { name: "propagates interface use through type children", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - use := b.Symbol("pkg.use") - typ := b.Symbol("_llgo_pkg.T") - child := b.Symbol("_llgo_pkg.Child") - iface := b.Symbol("_llgo_iface$I") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + use := b.sym("pkg.use") + typ := b.sym("_llgo_pkg.T") + child := b.sym("_llgo_pkg.Child") + iface := b.sym("_llgo_iface$I") mSig := methodSig(b, "M") - b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) - b.AddMethodInfo(typ, []metadata.MethodSlot{ + b.addIfaceEntry(iface, []pkgSig{mSig}) + b.addMethodInfo(typ, []pkgSlot{ methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), }) - b.AddMethodInfo(child, []metadata.MethodSlot{ + b.addMethodInfo(child, []pkgSlot{ methodSlot(b, mSig, "pkg.(*Child).M", "pkg.Child.M"), }) - b.AddTypeChild(typ, child) - b.AddEdge(main, use) - b.AddEdge(main, typ) - b.AddEdge(typ, child) - b.AddUseIface(main, []metadata.Symbol{typ}) - b.AddUseIfaceMethod(use, []metadata.IfaceMethodDemand{{ - Target: iface, - Sig: mSig, - }}) + b.b.AddTypeChild(typ, child) + b.addEdge(main, use) + b.addEdge(main, typ) + b.addEdge(typ, child) + b.addUseIface(main, typ) + b.addUseIfaceMethod(use, iface, mSig) }), want: map[string][]int{ "_llgo_pkg.Child": {0}, @@ -188,32 +173,29 @@ func TestAnalyze(t *testing.T) { { name: "value type entering interface semantics keeps pointer method implementation", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - unmarshal := b.Symbol("pkg.unmarshal") - containerPtr := b.Symbol("*_llgo_pkg.Container") - container := b.Symbol("_llgo_pkg.Container") - raw := b.Symbol("_llgo_pkg.RawMessage") - rawPtr := b.Symbol("*_llgo_pkg.RawMessage") - iface := b.Symbol("_llgo_pkg.Unmarshaler") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + unmarshal := b.sym("pkg.unmarshal") + containerPtr := b.sym("*_llgo_pkg.Container") + container := b.sym("_llgo_pkg.Container") + raw := b.sym("_llgo_pkg.RawMessage") + rawPtr := b.sym("*_llgo_pkg.RawMessage") + iface := b.sym("_llgo_pkg.Unmarshaler") unmarshalSig := methodSigWithType(b, "UnmarshalJSON", "_llgo_func$bytes_error") - b.AddIfaceEntry(iface, []metadata.MethodSig{unmarshalSig}) - b.AddMethodInfo(rawPtr, []metadata.MethodSlot{ + b.addIfaceEntry(iface, []pkgSig{unmarshalSig}) + b.addMethodInfo(rawPtr, []pkgSlot{ methodSlot(b, unmarshalSig, "pkg.(*RawMessage).UnmarshalJSON", "pkg.(*RawMessage).UnmarshalJSON"), }) - b.AddTypeChild(containerPtr, container) - b.AddTypeChild(container, raw) - b.AddEdge(main, unmarshal) - b.AddEdge(main, containerPtr) - b.AddEdge(containerPtr, container) - b.AddEdge(container, raw) - b.AddEdge(raw, rawPtr) - b.AddUseIface(main, []metadata.Symbol{containerPtr}) - b.AddUseIfaceMethod(unmarshal, []metadata.IfaceMethodDemand{{ - Target: iface, - Sig: unmarshalSig, - }}) + b.b.AddTypeChild(containerPtr, container) + b.b.AddTypeChild(container, raw) + b.addEdge(main, unmarshal) + b.addEdge(main, containerPtr) + b.addEdge(containerPtr, container) + b.addEdge(container, raw) + b.addEdge(raw, rawPtr) + b.addUseIface(main, containerPtr) + b.addUseIfaceMethod(unmarshal, iface, unmarshalSig) }), want: map[string][]int{"*_llgo_pkg.RawMessage": {0}}, }, @@ -225,21 +207,21 @@ func TestAnalyze(t *testing.T) { { name: "constant MethodByName keeps same-name method", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - use := b.Symbol("pkg.use") - typ := b.Symbol("_llgo_pkg.T") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + use := b.sym("pkg.use") + typ := b.sym("_llgo_pkg.T") mSig := methodSig(b, "M") nSig := methodSig(b, "N") - b.AddMethodInfo(typ, []metadata.MethodSlot{ + b.addMethodInfo(typ, []pkgSlot{ methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), methodSlot(b, nSig, "pkg.(*T).N", "pkg.T.N"), }) - b.AddEdge(main, use) - b.AddEdge(main, typ) - b.AddUseIface(main, []metadata.Symbol{typ}) - b.AddUseNamedMethod(use, []metadata.Name{mSig.Name}) + b.addEdge(main, use) + b.addEdge(main, typ) + b.addUseIface(main, typ) + b.b.AddNamedMethodEdge(use, mSig.name) }), want: map[string][]int{"_llgo_pkg.T": {0}}, }, @@ -252,23 +234,23 @@ func TestAnalyze(t *testing.T) { { name: "reflection keeps exported methods only", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - use := b.Symbol("pkg.use") - typ := b.Symbol("_llgo_pkg.T") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + use := b.sym("pkg.use") + typ := b.sym("_llgo_pkg.T") mSig := methodSig(b, "M") nSig := methodSig(b, "N") unexportedSig := methodSig(b, "m") - b.AddMethodInfo(typ, []metadata.MethodSlot{ + b.addMethodInfo(typ, []pkgSlot{ methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), methodSlot(b, nSig, "pkg.(*T).N", "pkg.T.N"), methodSlot(b, unexportedSig, "pkg.(*T).m", "pkg.T.m"), }) - b.AddEdge(main, use) - b.AddEdge(main, typ) - b.AddUseIface(main, []metadata.Symbol{typ}) - b.AddReflectMethod(use) + b.addEdge(main, use) + b.addEdge(main, typ) + b.addUseIface(main, typ) + b.b.MarkReflect(use) }), want: map[string][]int{"_llgo_pkg.T": {0, 1}}, }, @@ -285,41 +267,35 @@ func TestAnalyze(t *testing.T) { { name: "live method body can introduce new interface demands", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - useT := b.Symbol("pkg.useT") - callU := b.Symbol("pkg.callU") - useU := b.Symbol("pkg.useU") - typT := b.Symbol("_llgo_pkg.T") - typU := b.Symbol("_llgo_pkg.U") - ifaceI := b.Symbol("_llgo_iface$I") - ifaceJ := b.Symbol("_llgo_iface$J") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + useT := b.sym("pkg.useT") + callU := b.sym("pkg.callU") + useU := b.sym("pkg.useU") + typT := b.sym("_llgo_pkg.T") + typU := b.sym("_llgo_pkg.U") + ifaceI := b.sym("_llgo_iface$I") + ifaceJ := b.sym("_llgo_iface$J") mSig := methodSig(b, "M") nSig := methodSig(b, "N") - b.AddIfaceEntry(ifaceI, []metadata.MethodSig{mSig}) - b.AddIfaceEntry(ifaceJ, []metadata.MethodSig{nSig}) - b.AddMethodInfo(typT, []metadata.MethodSlot{ + b.addIfaceEntry(ifaceI, []pkgSig{mSig}) + b.addIfaceEntry(ifaceJ, []pkgSig{nSig}) + b.addMethodInfo(typT, []pkgSlot{ methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), }) - b.AddMethodInfo(typU, []metadata.MethodSlot{ + b.addMethodInfo(typU, []pkgSlot{ methodSlot(b, nSig, "pkg.(*U).N", "pkg.U.N"), }) - b.AddEdge(main, useT) - b.AddEdge(main, typT) - b.AddUseIface(main, []metadata.Symbol{typT}) - b.AddUseIfaceMethod(useT, []metadata.IfaceMethodDemand{{ - Target: ifaceI, - Sig: mSig, - }}) - b.AddEdge(b.Symbol("pkg.T.M"), callU) - b.AddEdge(callU, useU) - b.AddEdge(callU, typU) - b.AddUseIface(callU, []metadata.Symbol{typU}) - b.AddUseIfaceMethod(useU, []metadata.IfaceMethodDemand{{ - Target: ifaceJ, - Sig: nSig, - }}) + b.addEdge(main, useT) + b.addEdge(main, typT) + b.addUseIface(main, typT) + b.addUseIfaceMethod(useT, ifaceI, mSig) + b.addEdge(b.sym("pkg.T.M"), callU) + b.addEdge(callU, useU) + b.addEdge(callU, typU) + b.addUseIface(callU, typU) + b.addUseIfaceMethod(useU, ifaceJ, nSig) }), want: map[string][]int{ "_llgo_pkg.T": {0}, @@ -335,26 +311,23 @@ func TestAnalyze(t *testing.T) { { name: "interface demand and conversion from different reachable functions meet", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - init := b.Symbol("pkg.init") - toType := b.Symbol("pkg.toType") - typ := b.Symbol("_llgo_pkg.rtype") - iface := b.Symbol("_llgo_pkg.Type") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + init := b.sym("pkg.init") + toType := b.sym("pkg.toType") + typ := b.sym("_llgo_pkg.rtype") + iface := b.sym("_llgo_pkg.Type") elemSig := methodSig(b, "Elem") - b.AddIfaceEntry(iface, []metadata.MethodSig{elemSig}) - b.AddMethodInfo(typ, []metadata.MethodSlot{ + b.addIfaceEntry(iface, []pkgSig{elemSig}) + b.addMethodInfo(typ, []pkgSlot{ methodSlot(b, elemSig, "pkg.(*rtype).Elem", "pkg.rtype.Elem"), }) - b.AddEdge(main, init) - b.AddEdge(main, toType) - b.AddEdge(toType, typ) - b.AddUseIface(toType, []metadata.Symbol{typ}) - b.AddUseIfaceMethod(init, []metadata.IfaceMethodDemand{{ - Target: iface, - Sig: elemSig, - }}) + b.addEdge(main, init) + b.addEdge(main, toType) + b.addEdge(toType, typ) + b.addUseIface(toType, typ) + b.addUseIfaceMethod(init, iface, elemSig) }), want: map[string][]int{"_llgo_pkg.rtype": {0}}, }, @@ -365,37 +338,29 @@ func TestAnalyze(t *testing.T) { // func main() { init(); toType() } // func init() { reflectType.Elem() } // func toType() Type { return rtype{} } - // - // Some patched packages may report the same interface symbol with an - // alternate signature for one method name. That must not make the - // interface look larger than it is when checking whether rtype implements - // Type. { name: "duplicate interface method names do not inflate interface size", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - init := b.Symbol("pkg.init") - toType := b.Symbol("pkg.toType") - typ := b.Symbol("_llgo_pkg.rtype") - iface := b.Symbol("_llgo_pkg.Type") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + init := b.sym("pkg.init") + toType := b.sym("pkg.toType") + typ := b.sym("_llgo_pkg.rtype") + iface := b.sym("_llgo_pkg.Type") elemSig := methodSig(b, "Elem") kindSig := methodSigWithType(b, "Kind", "_llgo_func$kind") altKindSig := methodSigWithType(b, "Kind", "_llgo_func$altKind") - b.AddIfaceEntry(iface, []metadata.MethodSig{elemSig, kindSig, altKindSig}) - b.AddMethodInfo(typ, []metadata.MethodSlot{ + b.addIfaceEntry(iface, []pkgSig{elemSig, kindSig, altKindSig}) + b.addMethodInfo(typ, []pkgSlot{ methodSlot(b, elemSig, "pkg.(*rtype).Elem", "pkg.rtype.Elem"), methodSlot(b, kindSig, "pkg.(*rtype).Kind", "pkg.rtype.Kind"), }) - b.AddEdge(main, init) - b.AddEdge(main, toType) - b.AddEdge(toType, typ) - b.AddUseIface(toType, []metadata.Symbol{typ}) - b.AddUseIfaceMethod(init, []metadata.IfaceMethodDemand{{ - Target: iface, - Sig: elemSig, - }}) + b.addEdge(main, init) + b.addEdge(main, toType) + b.addEdge(toType, typ) + b.addUseIface(toType, typ) + b.addUseIfaceMethod(init, iface, elemSig) }), want: map[string][]int{"_llgo_pkg.rtype": {0}}, }, @@ -407,25 +372,22 @@ func TestAnalyze(t *testing.T) { { name: "ignores unreachable semantic facts", roots: []string{"pkg.main"}, - summary: buildPackage(func(b *metadata.Builder) { - main := b.Symbol("pkg.main") - unreachable := b.Symbol("pkg.unreachable") - typ := b.Symbol("_llgo_pkg.T") - iface := b.Symbol("_llgo_iface$I") + summary: buildPackage(func(b *pkgBuilder) { + main := b.sym("pkg.main") + unreachable := b.sym("pkg.unreachable") + typ := b.sym("_llgo_pkg.T") + iface := b.sym("_llgo_iface$I") mSig := methodSig(b, "M") - b.AddIfaceEntry(iface, []metadata.MethodSig{mSig}) - b.AddMethodInfo(typ, []metadata.MethodSlot{ + b.addIfaceEntry(iface, []pkgSig{mSig}) + b.addMethodInfo(typ, []pkgSlot{ methodSlot(b, mSig, "pkg.(*T).M", "pkg.T.M"), }) - b.AddEdge(main, typ) - b.AddUseIface(unreachable, []metadata.Symbol{typ}) - b.AddUseIfaceMethod(unreachable, []metadata.IfaceMethodDemand{{ - Target: iface, - Sig: mSig, - }}) - b.AddUseNamedMethod(unreachable, []metadata.Name{mSig.Name}) - b.AddReflectMethod(unreachable) + b.addEdge(main, typ) + b.addUseIface(unreachable, typ) + b.addUseIfaceMethod(unreachable, iface, mSig) + b.b.AddNamedMethodEdge(unreachable, mSig.name) + b.b.MarkReflect(unreachable) }), want: map[string][]int{}, }, @@ -441,41 +403,101 @@ func TestAnalyze(t *testing.T) { } } -type deadcodeCase struct { - name string - summary *metadata.PackageMeta - roots []string - want map[string][]int +// ── test builder helpers ────────────────────────────────────────────────────── + +type pkgSig struct { + name string + mtype meta.LocalSymbol } -func buildPackage(fn func(*metadata.Builder)) *metadata.PackageMeta { - b := metadata.NewBuilder() - fn(b) - return b.Build() +type pkgSlot struct { + sig pkgSig + ifn meta.LocalSymbol + tfn meta.LocalSymbol } -func methodSig(b *metadata.Builder, name string) metadata.MethodSig { - return methodSigWithType(b, name, "_llgo_func$X") +// pkgBuilder wraps meta.Builder and tracks iface method order so that +// addUseIfaceMethod can look up the sig index required by EdgeUseIfaceMethod. +type pkgBuilder struct { + b *meta.Builder + ifaceOrder map[meta.LocalSymbol][]pkgSig +} + +func newPkgBuilder() *pkgBuilder { + return &pkgBuilder{ + b: meta.NewBuilder(), + ifaceOrder: make(map[meta.LocalSymbol][]pkgSig), + } +} + +func (p *pkgBuilder) sym(name string) meta.LocalSymbol { return p.b.Sym(name) } + +func (p *pkgBuilder) addEdge(src, dst meta.LocalSymbol) { + p.b.AddEdge(src, dst, meta.EdgeOrdinary, 0) +} + +func (p *pkgBuilder) addUseIface(fn, typ meta.LocalSymbol) { + p.b.AddEdge(fn, typ, meta.EdgeUseIface, 0) +} + +// addUseIfaceMethod records a demand for iface.sig from fn. The sig index is +// determined by the order in which sigs were registered via addIfaceEntry. +func (p *pkgBuilder) addUseIfaceMethod(fn, iface meta.LocalSymbol, sig pkgSig) { + sigs := p.ifaceOrder[iface] + for i, s := range sigs { + if s.name == sig.name && s.mtype == sig.mtype { + p.b.AddEdge(fn, iface, meta.EdgeUseIfaceMethod, uint32(i)) + return + } + } + panic("addUseIfaceMethod: sig not found in iface — call addIfaceEntry first") } -func methodSigWithType(b *metadata.Builder, name, mtype string) metadata.MethodSig { - return metadata.MethodSig{ - Name: b.Name(name), - MType: b.Symbol(mtype), +func (p *pkgBuilder) addIfaceEntry(iface meta.LocalSymbol, sigs []pkgSig) { + p.ifaceOrder[iface] = sigs + for _, sig := range sigs { + p.b.AddIfaceMethod(iface, sig.name, sig.mtype) } } -func methodSlot(b *metadata.Builder, sig metadata.MethodSig, ifn, tfn string) metadata.MethodSlot { - return metadata.MethodSlot{ - Sig: sig, - IFn: b.Symbol(ifn), - TFn: b.Symbol(tfn), +func (p *pkgBuilder) addMethodInfo(typ meta.LocalSymbol, slots []pkgSlot) { + for _, slot := range slots { + p.b.AddMethodSlot(typ, slot.sig.name, slot.sig.mtype, slot.ifn, slot.tfn) + } +} + +func methodSig(b *pkgBuilder, name string) pkgSig { + return methodSigWithType(b, name, "_llgo_func$X") +} + +func methodSigWithType(b *pkgBuilder, name, mtype string) pkgSig { + return pkgSig{name: name, mtype: b.sym(mtype)} +} + +func methodSlot(b *pkgBuilder, sig pkgSig, ifn, tfn string) pkgSlot { + return pkgSlot{sig: sig, ifn: b.sym(ifn), tfn: b.sym(tfn)} +} + +type deadcodeCase struct { + name string + summary *meta.PackageMeta + roots []string + want map[string][]int +} + +func buildPackage(fn func(*pkgBuilder)) *meta.PackageMeta { + b := newPkgBuilder() + fn(b) + pm, err := b.b.Build() + if err != nil { + panic("buildPackage: " + err.Error()) } + return pm } -func newSummary(t *testing.T, pkgs ...*metadata.PackageMeta) *metadata.GlobalSummary { +func newSummary(t *testing.T, pkgs ...*meta.PackageMeta) *meta.GlobalSummary { t.Helper() - summary, err := metadata.NewGlobalSummary(pkgs) + summary, err := meta.NewGlobalSummary(pkgs) if err != nil { t.Fatalf("NewGlobalSummary: %v", err) } diff --git a/internal/meta/builder.go b/internal/meta/builder.go index 81cbf73ab6..59e8c268b4 100644 --- a/internal/meta/builder.go +++ b/internal/meta/builder.go @@ -48,7 +48,8 @@ type Builder struct { edges [][]bEdge // per-symbol TypeChildren lists - typeChildren [][]LocalSymbol + typeChildren [][]LocalSymbol + typeChildrenSet map[[2]LocalSymbol]struct{} // dedup (parent, child) pairs // per-symbol MethodInfo (only concrete types) methodInfo [][]bMethodSlot @@ -86,8 +87,9 @@ type bMethodSig struct { // NewBuilder creates an empty Builder. func NewBuilder() *Builder { return &Builder{ - strMap: make(map[string]uint32), - symMap: make(map[string]LocalSymbol), + strMap: make(map[string]uint32), + symMap: make(map[string]LocalSymbol), + typeChildrenSet: make(map[[2]LocalSymbol]struct{}), } } @@ -159,7 +161,13 @@ func (b *Builder) AddNamedMethodEdge(src LocalSymbol, methodName string) { } // AddTypeChild records that parent type structurally contains child type. +// Idempotent: duplicate (parent, child) pairs are silently ignored. func (b *Builder) AddTypeChild(parent, child LocalSymbol) { + key := [2]LocalSymbol{parent, child} + if _, ok := b.typeChildrenSet[key]; ok { + return + } + b.typeChildrenSet[key] = struct{}{} b.typeChildren[parent] = append(b.typeChildren[parent], child) } @@ -175,11 +183,19 @@ func (b *Builder) AddMethodSlot(typ LocalSymbol, methodName string, mtype, ifn, } // AddIfaceMethod records one method in an interface's method set. -// Methods must be appended in declaration order. +// Idempotent: if the same (name, mtype) pair is already registered for iface, +// this call is a no-op — the Builder deduplicates internally. func (b *Builder) AddIfaceMethod(iface LocalSymbol, methodName string, mtype LocalSymbol) { + ref := b.internName(methodName) + mt := uint32(mtype) + for _, s := range b.ifaceInfo[iface] { + if s.name == ref && s.mtype == mt { + return + } + } b.ifaceInfo[iface] = append(b.ifaceInfo[iface], bMethodSig{ - name: b.internName(methodName), - mtype: uint32(mtype), + name: ref, + mtype: mt, }) } diff --git a/internal/meta/format.go b/internal/meta/format.go new file mode 100644 index 0000000000..c676a93bf1 --- /dev/null +++ b/internal/meta/format.go @@ -0,0 +1,177 @@ +package meta + +import ( + "fmt" + "sort" + "strings" +) + +// MetaString formats pm as a human-readable string for testing and debugging. +func MetaString(pm *PackageMeta) string { + if pm == nil { + return "" + } + var sb strings.Builder + FormatMeta(&sb, pm) + return sb.String() +} + +// FormatMeta writes a human-readable representation of pm to w, +// grouped by section (TypeChildren, OrdinaryEdges, UseIface, etc.) +// to match the original metadata format used by golden file tests. +func FormatMeta(w *strings.Builder, pm *PackageMeta) { + n := pm.NSyms() + symName := func(sym LocalSymbol) string { return pm.SymbolName(sym) } + + // collect per-sym edge lists by kind + type kindMap = map[string][]string // src → []dst (sorted) + ordinary := make(map[string][]string) + useIface := make(map[string][]string) + useIfaceMethod := make(map[string][]string) // src → ["iface[idx]", ...] + useNamed := make(map[string][]string) + + for i := LocalSymbol(0); i < LocalSymbol(n); i++ { + src := symName(i) + for _, e := range pm.Edges(i) { + switch e.Kind { + case EdgeOrdinary: + ordinary[src] = append(ordinary[src], symName(LocalSymbol(e.Target))) + case EdgeUseIface: + useIface[src] = append(useIface[src], symName(LocalSymbol(e.Target))) + case EdgeUseIfaceMethod: + ifaceSym := LocalSymbol(e.Target) + iface := symName(ifaceSym) + sigs := pm.IfaceMethods(ifaceSym) + if int(e.Extra) < len(sigs) { + s := sigs[e.Extra] + useIfaceMethod[src] = append(useIfaceMethod[src], + fmt.Sprintf("%s %s %s", iface, pm.NameString(s.Name), symName(s.MType))) + } else { + useIfaceMethod[src] = append(useIfaceMethod[src], + fmt.Sprintf("%s[%d]", iface, e.Extra)) + } + case EdgeUseNamedMethod: + name := pm.NameString(NameRef{Off: e.Target, Len: e.Extra}) + useNamed[src] = append(useNamed[src], name) + } + } + } + + // collect TypeChildren + typeChildren := make(map[string][]string) + for i := LocalSymbol(0); i < LocalSymbol(n); i++ { + parent := symName(i) + for _, c := range pm.TypeChildren(i) { + typeChildren[parent] = append(typeChildren[parent], symName(c)) + } + } + + // collect MethodInfo + type slotInfo struct{ name, mtype, ifn, tfn string } + methodInfo := make(map[string][]slotInfo) + for i := LocalSymbol(0); i < LocalSymbol(n); i++ { + typ := symName(i) + for _, s := range pm.MethodSlots(i) { + methodInfo[typ] = append(methodInfo[typ], slotInfo{ + name: pm.NameString(s.Name), + mtype: symName(s.MType), + ifn: symName(s.IFn), + tfn: symName(s.TFn), + }) + } + } + + // collect InterfaceInfo + type sigInfo struct{ name, mtype string } + ifaceInfo := make(map[string][]sigInfo) + for i := LocalSymbol(0); i < LocalSymbol(n); i++ { + iface := symName(i) + for _, s := range pm.IfaceMethods(i) { + ifaceInfo[iface] = append(ifaceInfo[iface], sigInfo{ + name: pm.NameString(s.Name), + mtype: symName(s.MType), + }) + } + } + + // collect Reflect + var reflectSyms []string + for i := LocalSymbol(0); i < LocalSymbol(n); i++ { + if pm.HasReflect(i) { + reflectSyms = append(reflectSyms, symName(i)) + } + } + + printSection := func(title string, m map[string][]string) { + if len(m) == 0 { + return + } + fmt.Fprintf(w, "[%s]\n", title) + keys := sortedKeys(m) + for _, k := range keys { + vals := m[k] + sort.Strings(vals) + fmt.Fprintf(w, "%s:\n", k) + for _, v := range vals { + fmt.Fprintf(w, " %s\n", v) + } + } + fmt.Fprintln(w) + } + + printSection("TypeChildren", typeChildren) + printSection("OrdinaryEdges", ordinary) + printSection("UseIface", useIface) + printSection("UseIfaceMethod", useIfaceMethod) + printSection("UseNamedMethod", useNamed) + + if len(methodInfo) > 0 { + fmt.Fprintln(w, "[MethodInfo]") + keys := make([]string, 0, len(methodInfo)) + for k := range methodInfo { + keys = append(keys, k) + } + sort.Strings(keys) + for _, typ := range keys { + fmt.Fprintf(w, "%s:\n", typ) + for idx, s := range methodInfo[typ] { + fmt.Fprintf(w, " %d %s %s %s %s\n", idx, s.name, s.mtype, s.ifn, s.tfn) + } + } + fmt.Fprintln(w) + } + + if len(ifaceInfo) > 0 { + fmt.Fprintln(w, "[InterfaceInfo]") + keys := make([]string, 0, len(ifaceInfo)) + for k := range ifaceInfo { + keys = append(keys, k) + } + sort.Strings(keys) + for _, iface := range keys { + fmt.Fprintf(w, "%s:\n", iface) + for _, s := range ifaceInfo[iface] { + fmt.Fprintf(w, " %s %s\n", s.name, s.mtype) + } + } + fmt.Fprintln(w) + } + + if len(reflectSyms) > 0 { + sort.Strings(reflectSyms) + fmt.Fprintln(w, "[Reflect]") + for _, r := range reflectSyms { + fmt.Fprintf(w, " %s\n", r) + } + fmt.Fprintln(w) + } +} + +func sortedKeys(m map[string][]string) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} diff --git a/internal/meta/meta.go b/internal/meta/meta.go index 74bc37c047..7723cda948 100644 --- a/internal/meta/meta.go +++ b/internal/meta/meta.go @@ -124,7 +124,9 @@ func (pm *PackageMeta) Close() error { // NSyms returns the number of symbols in this package. func (pm *PackageMeta) NSyms() uint32 { return pm.nsyms } -// SymbolName returns the name of sym by reading directly from the string table. +// SymbolName returns the name of sym as a zero-copy view into the string table. +// The returned string points directly into the mmap region and is only valid for +// the lifetime of pm — do not retain it after Close. func (pm *PackageMeta) SymbolName(sym LocalSymbol) string { if uint32(sym) >= pm.nsyms { return "" @@ -133,12 +135,14 @@ func (pm *PackageMeta) SymbolName(sym LocalSymbol) string { base := pm.symOff + 4 + uint32(sym)*recSize nameOff := binary.LittleEndian.Uint32(pm.raw[base+0:]) nameLen := binary.LittleEndian.Uint32(pm.raw[base+4:]) - return string(pm.raw[pm.strOff+nameOff : pm.strOff+nameOff+nameLen]) + return unsafe.String(&pm.raw[pm.strOff+nameOff], int(nameLen)) } -// NameString returns the string referenced by a NameRef. +// NameString returns the string referenced by a NameRef as a zero-copy view +// into the string table. The returned string points directly into the mmap +// region and is only valid for the lifetime of pm — do not retain it after Close. func (pm *PackageMeta) NameString(ref NameRef) string { - return string(pm.raw[pm.strOff+ref.Off : pm.strOff+ref.Off+ref.Len]) + return unsafe.String(&pm.raw[pm.strOff+ref.Off], int(ref.Len)) } // Edges returns all edges from sym as a zero-copy view into the mmap region: diff --git a/ssa/abitype.go b/ssa/abitype.go index 67c702a45f..0e77bed2fc 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -26,8 +26,6 @@ import ( "github.com/goplus/llgo/ssa/abi" "github.com/xgo-dev/llvm" - - "github.com/goplus/llgo/internal/metadata" ) // ----------------------------------------------------------------------------- @@ -350,10 +348,10 @@ func (b Builder) recordTypeChildren(parentName string, t types.Type) { if mb == nil { return } - parent := mb.Symbol(parentName) + parent := mb.Sym(parentName) for _, child := range b.directTypeChildren(t) { childName, _ := b.Prog.abi.TypeName(child) - mb.AddTypeChild(parent, mb.Symbol(childName)) + mb.AddTypeChild(parent, mb.Sym(childName)) } } @@ -501,11 +499,7 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va ft := prog.rtType("Method") n := mset.Len() fields := make([]llvm.Value, n) - var slots []metadata.MethodSlot typeName, _ := prog.abi.TypeName(t) - if b.Pkg.MetaBuilder != nil { - slots = make([]metadata.MethodSlot, 0, n) - } pkg, _ := b.abiUncommonPkg(t) anonymous := pkg == nil if anonymous { @@ -540,19 +534,9 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va fields[i] = llvm.ConstNamedStruct(ft.ll, values) if mb := b.Pkg.MetaBuilder; mb != nil { mtypeName, _ := prog.abi.TypeName(ftyp) - slots = append(slots, metadata.MethodSlot{ - Sig: metadata.MethodSig{ - Name: mb.Name(fullName), - MType: mb.Symbol(mtypeName), - }, - IFn: mb.Symbol(ifn.Name()), - TFn: mb.Symbol(tfn.Name()), - }) + mb.AddMethodSlot(mb.Sym(typeName), fullName, mb.Sym(mtypeName), mb.Sym(ifn.Name()), mb.Sym(tfn.Name())) } } - if mb := b.Pkg.MetaBuilder; mb != nil { - mb.AddMethodInfo(mb.Symbol(typeName), slots) - } return llvm.ConstArray(ft.ll, fields) } diff --git a/ssa/expr.go b/ssa/expr.go index c308812eeb..78d8385189 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -24,7 +24,6 @@ import ( "go/types" "log" - "github.com/goplus/llgo/internal/metadata" "github.com/xgo-dev/llvm" ) @@ -1307,13 +1306,13 @@ func (b Builder) checkReflect(fn Expr, args []Expr) { pkg.MethodByIndex[v] = none{} pkg.NeedAbiInit |= ReflectMethodByIndex if mb := pkg.MetaBuilder; mb != nil { - mb.AddReflectMethod(mb.Symbol(b.Func.Name())) + mb.MarkReflect(mb.Sym(b.Func.Name())) } return } pkg.NeedAbiInit |= ReflectMethodDynamic if mb := pkg.MetaBuilder; mb != nil { - mb.AddReflectMethod(mb.Symbol(b.Func.Name())) + mb.MarkReflect(mb.Sym(b.Func.Name())) } } case "reflect.Value.MethodByName": @@ -1325,13 +1324,13 @@ func (b Builder) checkReflect(fn Expr, args []Expr) { pkg.MethodByName[v] = none{} pkg.NeedAbiInit |= ReflectMethodByName if mb := pkg.MetaBuilder; mb != nil { - mb.AddUseNamedMethod(mb.Symbol(b.Func.Name()), []metadata.Name{mb.Name(v)}) + mb.AddNamedMethodEdge(mb.Sym(b.Func.Name()), v) } return } pkg.NeedAbiInit |= ReflectMethodDynamic if mb := pkg.MetaBuilder; mb != nil { - mb.AddReflectMethod(mb.Symbol(b.Func.Name())) + mb.MarkReflect(mb.Sym(b.Func.Name())) } } } diff --git a/ssa/interface.go b/ssa/interface.go index c120e24b4b..b658417b0a 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -20,7 +20,7 @@ import ( "go/token" "go/types" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/ssa/abi" "github.com/xgo-dev/llvm" ) @@ -82,26 +82,16 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { tclosure := prog.Type(sig, InGo) i := iMethodOf(rawIntf, method.Name()) if mb := b.Pkg.MetaBuilder; mb != nil { - intfTypeName, _ := prog.abi.TypeName(intf.raw.Type) - mtypeName, _ := prog.abi.TypeName(funcType(prog, method.Type())) - mb.AddUseIfaceMethod(mb.Symbol(b.Func.Name()), []metadata.IfaceMethodDemand{{ - Target: mb.Symbol(intfTypeName), - Sig: metadata.MethodSig{ - Name: mb.Name(mthName(method)), - MType: mb.Symbol(mtypeName), - }, - }}) - - methods := make([]metadata.MethodSig, 0, rawIntf.NumMethods()) - for i := 0; i < rawIntf.NumMethods(); i++ { - im := rawIntf.Method(i) + intfSym := mb.Sym(func() string { n, _ := prog.abi.TypeName(intf.raw.Type); return n }()) + // Record which interface method is demanded. i is the method's index in + // rawIntf (same order we register methods below), so it becomes extra. + mb.AddEdge(mb.Sym(b.Func.Name()), intfSym, meta.EdgeUseIfaceMethod, uint32(i)) + // Register all interface methods — AddIfaceMethod is idempotent per (name, mtype). + for j := 0; j < rawIntf.NumMethods(); j++ { + im := rawIntf.Method(j) imtypeName, _ := prog.abi.TypeName(funcType(prog, im.Type())) - methods = append(methods, metadata.MethodSig{ - Name: mb.Name(mthName(im)), - MType: mb.Symbol(imtypeName), - }) + mb.AddIfaceMethod(intfSym, mthName(im), mb.Sym(imtypeName)) } - mb.AddIfaceEntry(mb.Symbol(intfTypeName), methods) } data := b.InlineCall(b.Pkg.rtFunc("IfacePtrData"), intf) impl := intf.impl @@ -199,7 +189,7 @@ func (b Builder) recordUseIface(typ Type) { if mb := b.Pkg.MetaBuilder; mb != nil { if _, ok := types.Unalias(typ.raw.Type).Underlying().(*types.Interface); !ok { typeName, _ := b.Prog.abi.TypeName(typ.raw.Type) - mb.AddUseIface(mb.Symbol(b.Func.Name()), []metadata.Symbol{mb.Symbol(typeName)}) + mb.AddEdge(mb.Sym(b.Func.Name()), mb.Sym(typeName), meta.EdgeUseIface, 0) } } } diff --git a/ssa/package.go b/ssa/package.go index 7cb9114fbf..eb0a7c6869 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -26,7 +26,7 @@ import ( "unsafe" "github.com/goplus/llgo/internal/env" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/ssa/abi" "github.com/xgo-dev/llvm" "golang.org/x/tools/go/types/typeutil" @@ -734,7 +734,7 @@ type aPackage struct { NeedAbiInit int // bitmask of Reflect* flags indicating which reflect type-construction operations are used MethodByIndex map[int]none MethodByName map[string]none - MetaBuilder *metadata.Builder + MetaBuilder *meta.Builder export map[string]string // pkgPath.nameInPkg => exportname preserveSyms map[string]struct{} // set of exported symbol names From 9861150b4304f6b662e97e6a3ed70ad04eb86717 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sat, 27 Jun 2026 18:47:05 +0800 Subject: [PATCH 17/37] perf: make MethodSlots/InterfaceMethods lazy in GlobalSummary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove Phase 2 eager translation — instead of scanning every type in all 334 packages and translating all ~10080 method slots up front, only mark concrete/interface type kinds in Phase 1 (cheap CSR range checks, no slot translation). MethodSlots() and InterfaceMethods() now translate lazily on first access and cache the result. DCE typically reaches only ~500 of ~10000+ types, so lazy translation avoids thousands of unnecessary pm.NameString + internName calls. Perf (etcd client demo, 334 packages): merge: 115ms → ~18ms (6.4x faster) total: ~149ms → ~48ms (3.1x faster) Co-Authored-By: Claude --- internal/meta/global.go | 109 ++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/internal/meta/global.go b/internal/meta/global.go index 7a960f0d17..fd41e6d220 100644 --- a/internal/meta/global.go +++ b/internal/meta/global.go @@ -5,11 +5,9 @@ package meta // // Merge strategy: // - Symbols are interned into a global Symbol space; each package's local -// symbols are mapped via locToGlb. Edges and TypeChildren are NOT rewritten -// at merge time — they are translated lazily on query (the bulk of the data, -// and the main cost we avoid up front). -// - MethodInfo / InterfaceInfo / reflect are small, so they are translated -// eagerly at merge time, interning method names into the global Name space. +// symbols are mapped via locToGlb. Edges, TypeChildren, MethodSlots and +// InterfaceMethods are NOT rewritten at merge time — they are translated +// lazily on query. Only the strings are interned up front. // - Duplicate symbols (e.g. linkonce type descriptors emitted by several // packages) are first-wins: the first package that owns facts for a symbol // becomes its owner; later duplicates are ignored. @@ -26,10 +24,14 @@ type GlobalSummary struct { nameIntern map[string]Name nameStrings []string // Name → text - // eagerly translated small sections + // per-type flags set at merge time (no translation, just CSR range checks) + isConcrete []bool // global Symbol → true if has method slots + isInterface []bool // global Symbol → true if has iface methods + reflect map[Symbol]struct{} + + // lazily translated, cached on first access methodInfo map[Symbol][]GMethodSlot interfaceInfo map[Symbol][]GMethodSig - reflect map[Symbol]struct{} interfaces []Symbol concreteTypes []Symbol @@ -69,6 +71,14 @@ type symLoc struct { } // NewGlobalSummary merges package-local metadata into a whole-program view. +// +// Phase 1 interns all symbol names and builds the locToGlb mapping, owner +// indices, and type-kind flags. No per-symbol data is translated — only +// string interning and CSR range checks happen here. +// +// MethodSlots / InterfaceMethods / reflect are translated lazily on first +// access and cached. This avoids translating thousands of unused slots in +// the common case where DCE only reaches a fraction of all types. func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { g := &GlobalSummary{ pkgs: pkgs, @@ -80,8 +90,8 @@ func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { reflect: make(map[Symbol]struct{}), } - // Phase 1: intern symbols, build locToGlb and owner (used for lazy - // edges/children translation). Touches no edges. + // Phase 1: intern symbols, build locToGlb and owner, mark type kinds. + // Touches no edges, translates no slot/sig data. for pi, pm := range pkgs { if pm == nil { continue @@ -94,36 +104,21 @@ func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { if g.owner[gs].pkg < 0 && hasFacts(pm, li) { g.owner[gs] = symLoc{pkg: int32(pi), local: li} } - } - g.locToGlb[pi] = tab - } - // Phase 2: eagerly translate small sections (method/iface/reflect), - // interning method names into the global Name space. First-wins on dups. - for pi, pm := range pkgs { - if pm == nil { - continue - } - tab := g.locToGlb[pi] - n := pm.NSyms() - for li := LocalSymbol(0); li < LocalSymbol(n); li++ { - gs := tab[li] - if pm.IsConcreteType(li) { - if _, done := g.methodInfo[gs]; !done { - g.methodInfo[gs] = g.translateSlots(tab, pm, li) - g.concreteTypes = append(g.concreteTypes, gs) - } + // mark type kinds (no translation, just CSR range checks) + if pm.IsConcreteType(li) && !g.isConcrete[gs] { + g.isConcrete[gs] = true + g.concreteTypes = append(g.concreteTypes, gs) } - if pm.IsInterface(li) { - if _, done := g.interfaceInfo[gs]; !done { - g.interfaceInfo[gs] = g.translateSigs(tab, pm, li) - g.interfaces = append(g.interfaces, gs) - } + if pm.IsInterface(li) && !g.isInterface[gs] { + g.isInterface[gs] = true + g.interfaces = append(g.interfaces, gs) } if pm.HasReflect(li) { g.reflect[gs] = struct{}{} } } + g.locToGlb[pi] = tab } return g, nil } @@ -146,6 +141,8 @@ func (g *GlobalSummary) internSymbol(s string) Symbol { g.symIntern[s] = id g.symStrings = append(g.symStrings, s) g.owner = append(g.owner, symLoc{pkg: -1}) + g.isConcrete = append(g.isConcrete, false) + g.isInterface = append(g.isInterface, false) return id } @@ -159,6 +156,18 @@ func (g *GlobalSummary) internName(s string) Name { return id } +// ownerData returns the owning package and locToGlb table for sym. +func (g *GlobalSummary) ownerData(sym Symbol) (*PackageMeta, []Symbol, LocalSymbol) { + if int(sym) >= len(g.owner) { + return nil, nil, 0 + } + loc := g.owner[sym] + if loc.pkg < 0 { + return nil, nil, 0 + } + return g.pkgs[loc.pkg], g.locToGlb[loc.pkg], loc.local +} + func (g *GlobalSummary) translateSlots(tab []Symbol, pm *PackageMeta, li LocalSymbol) []GMethodSlot { local := pm.MethodSlots(li) out := make([]GMethodSlot, len(local)) @@ -217,13 +226,37 @@ func (g *GlobalSummary) Interfaces() []Symbol { return g.interfaces } // ConcreteTypes returns all concrete type symbols with method slots. func (g *GlobalSummary) ConcreteTypes() []Symbol { return g.concreteTypes } -// ── eager small sections ────────────────────────────────────────────────────── +// ── lazy per-type queries ───────────────────────────────────────────────────── -// MethodSlots returns the ABI method slots for concrete type typ. Read-only. -func (g *GlobalSummary) MethodSlots(typ Symbol) []GMethodSlot { return g.methodInfo[typ] } +// MethodSlots returns the ABI method slots for concrete type typ. +// Translated lazily on first access, cached thereafter. +func (g *GlobalSummary) MethodSlots(typ Symbol) []GMethodSlot { + if slots, ok := g.methodInfo[typ]; ok { + return slots + } + pm, tab, li := g.ownerData(typ) + if pm == nil { + return nil + } + slots := g.translateSlots(tab, pm, li) + g.methodInfo[typ] = slots + return slots +} -// InterfaceMethods returns the method set for interface iface. Read-only. -func (g *GlobalSummary) InterfaceMethods(iface Symbol) []GMethodSig { return g.interfaceInfo[iface] } +// InterfaceMethods returns the method set for interface iface. +// Translated lazily on first access, cached thereafter. +func (g *GlobalSummary) InterfaceMethods(iface Symbol) []GMethodSig { + if sigs, ok := g.interfaceInfo[iface]; ok { + return sigs + } + pm, tab, li := g.ownerData(iface) + if pm == nil { + return nil + } + sigs := g.translateSigs(tab, pm, li) + g.interfaceInfo[iface] = sigs + return sigs +} // HasReflectMethod reports whether sym triggers conservative reflection handling. func (g *GlobalSummary) HasReflectMethod(sym Symbol) bool { @@ -280,7 +313,7 @@ func (g *GlobalSummary) UseIfaceMethod(sym Symbol) []IfaceMethodDemand { continue } iface := tab[e.Target] - sigs := g.interfaceInfo[iface] + sigs := g.InterfaceMethods(iface) if int(e.Extra) < len(sigs) { out = append(out, IfaceMethodDemand{Target: iface, Sig: sigs[e.Extra]}) } From 3c14a84e418038c5e2421a678c8f58f41f9ebf1d Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sat, 27 Jun 2026 22:28:49 +0800 Subject: [PATCH 18/37] refactor: make PackageMeta an internal view; add N-prefix count methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PackageMeta query methods are now all lowercase (private). External code interacts only through GlobalSummary, Builder, ReadMeta, Bytes, and Close. - Drop IsConcreteType, IsInterface, IsCompositeType — callers use nmethodSlot/ntypeChild/nifaceMethod count methods instead - Add N-prefix count methods (Go goobj convention): nedge, ntypeChild, nmethodSlot, nifaceMethod — single CSR range check, no unsafe pointer - All CSR accessors share csrSlice[T] generic helper, eliminating repeated CSR+unsafe pointer boilerplate - Remove NSyms() method — same-package callers use pm.nsyms field - Remove NewPackageMetaFromBytes — no external callers - Make buildMethodImplKeys fully lazy: concrete type methodImplKeys are computed on-demand in flood's usedInIface path; only methodRefs reverse index is built eagerly - MetaString → String() (fmt.Stringer) — zero additional public API - Drop ConcreteTypes() from GlobalSummary; isConcrete tracking removed from Phase 1 Co-Authored-By: Claude --- cl/cltest/cltest.go | 3 +- internal/deadcode/analyze.go | 103 +++++++++++++++-------- internal/meta/format.go | 34 ++++---- internal/meta/global.go | 44 ++++------ internal/meta/global_test.go | 8 +- internal/meta/meta.go | 154 ++++++++++++++--------------------- internal/meta/meta_test.go | 112 +++++++++++++------------ 7 files changed, 222 insertions(+), 236 deletions(-) diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index d7b5ed0860..ad697d1fd0 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -39,7 +39,6 @@ import ( "github.com/goplus/llgo/internal/build" "github.com/goplus/llgo/internal/littest" "github.com/goplus/llgo/internal/llgen" - pkgmeta "github.com/goplus/llgo/internal/meta" "github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/ssa/ssatest" "github.com/qiniu/x/test" @@ -357,7 +356,7 @@ func withModuleCapture(conf *build.Config, pkgDir string) (*build.Config, *strin return filepath.Dir(file) == pkgDir }) { module = pkg.LPkg.String() - meta = pkgmeta.MetaString(pkg.Meta) + meta = pkg.Meta.String() } } return &localConf, &module, &meta diff --git a/internal/deadcode/analyze.go b/internal/deadcode/analyze.go index 40418b53f2..1fed7a958e 100644 --- a/internal/deadcode/analyze.go +++ b/internal/deadcode/analyze.go @@ -1,8 +1,11 @@ package deadcode import ( + "fmt" "go/token" + "os" "sort" + "time" "github.com/goplus/llgo/internal/meta" ) @@ -31,8 +34,10 @@ type methodRef struct { type pass struct { info *meta.GlobalSummary - methodImplKeys map[methodID][]ifaceMethodKey - typeSymbols map[meta.Symbol]struct{} + methodImplKeys map[methodID][]ifaceMethodKey + methodRefs map[meta.GMethodSig][]meta.Symbol // sig → []iface (built eagerly) + ifaceMethodCounts map[meta.Symbol]int // iface → unique method name count + typeSymbols map[meta.Symbol]struct{} reachable map[meta.Symbol]struct{} usedInIface map[meta.Symbol]struct{} @@ -72,6 +77,8 @@ func deadcode(info *meta.GlobalSummary, roots []meta.Symbol) map[meta.Symbol][]i d := &pass{ info: info, methodImplKeys: make(map[methodID][]ifaceMethodKey), + methodRefs: make(map[meta.GMethodSig][]meta.Symbol), + ifaceMethodCounts: make(map[meta.Symbol]int), typeSymbols: make(map[meta.Symbol]struct{}), reachable: make(map[meta.Symbol]struct{}), usedInIface: make(map[meta.Symbol]struct{}), @@ -81,7 +88,7 @@ func deadcode(info *meta.GlobalSummary, roots []meta.Symbol) map[meta.Symbol][]i markedMethods: make(map[methodID]struct{}), liveSlots: make(map[meta.Symbol][]int), } - d.buildMethodImplKeys() + d.buildMethodRefs() for _, root := range roots { d.markReachable(root) @@ -96,48 +103,66 @@ func deadcode(info *meta.GlobalSummary, roots []meta.Symbol) map[meta.Symbol][]i } } -func (d *pass) buildMethodImplKeys() { - methodRefs := make(map[meta.GMethodSig][]meta.Symbol) - ifaceMethodCounts := make(map[meta.Symbol]int) +// buildMethodRefs builds the methodRefs reverse index (sig → []iface) from all +// interfaces. This is cheap (tens of interfaces, hundreds of sigs) and must be +// eager — every concrete type needs it to check implementation relationships. +// +// Concrete type methodImplKeys are NOT computed here. They are built lazily in +// computeMethodImplKeys when a type first enters usedInIface. +func (d *pass) buildMethodRefs() { + t0 := time.Now() for _, iface := range d.info.Interfaces() { d.typeSymbols[iface] = struct{}{} seenNames := make(map[meta.Name]struct{}) for _, sig := range d.info.InterfaceMethods(iface) { - methodRefs[sig] = appendSymbolUnique(methodRefs[sig], iface) + d.methodRefs[sig] = appendSymbolUnique(d.methodRefs[sig], iface) if _, ok := seenNames[sig.Name]; ok { continue } seenNames[sig.Name] = struct{}{} - ifaceMethodCounts[iface]++ + d.ifaceMethodCounts[iface]++ } } + t1 := time.Now() + fmt.Fprintf(os.Stderr, "[dce] methodRefs index: interfaces=%d total_sigs=%d %v\n", + len(d.info.Interfaces()), len(d.methodRefs), t1.Sub(t0)) +} - for _, typ := range d.info.ConcreteTypes() { - d.typeSymbols[typ] = struct{}{} - slots := d.info.MethodSlots(typ) - impls := make(map[meta.Symbol]int) - seen := make(map[ifaceMethodName]struct{}) - - for _, slot := range slots { - sig := meta.GMethodSig{Name: slot.Name, MType: slot.MType} - for _, iface := range methodRefs[sig] { - key := ifaceMethodName{iface: iface, name: slot.Name} - if _, ok := seen[key]; ok { - continue - } - seen[key] = struct{}{} - impls[iface]++ +// computeMethodImplKeys lazily builds methodImplKeys for a single concrete type +// that has entered usedInIface. Called at most once per type. +func (d *pass) computeMethodImplKeys(typ meta.Symbol, slots []meta.GMethodSlot) { + if _, done := d.methodImplKeys[methodID{owner: typ, slot: 0}]; done { + // Already computed — check skip by looking at slot 0. If slot 0 has + // an entry, the whole type was processed (we always process all slots + // of a type at once). + // Fine print: a type with 0 slots will never reach here because + // markUsedInIface only calls this when slots is non-empty. + return + } + // Mark the type and compute all slots at once. + d.typeSymbols[typ] = struct{}{} + impls := make(map[meta.Symbol]int) + seen := make(map[ifaceMethodName]struct{}) + + for _, slot := range slots { + sig := meta.GMethodSig{Name: slot.Name, MType: slot.MType} + for _, iface := range d.methodRefs[sig] { + key := ifaceMethodName{iface: iface, name: slot.Name} + if _, ok := seen[key]; ok { + continue } + seen[key] = struct{}{} + impls[iface]++ } + } - for slotIndex, slot := range slots { - id := methodID{owner: typ, slot: slotIndex} - sig := meta.GMethodSig{Name: slot.Name, MType: slot.MType} - for _, iface := range methodRefs[sig] { - if impls[iface] == ifaceMethodCounts[iface] { - key := ifaceMethodKey{iface: iface, sig: sig} - d.methodImplKeys[id] = append(d.methodImplKeys[id], key) - } + for slotIndex, slot := range slots { + id := methodID{owner: typ, slot: slotIndex} + sig := meta.GMethodSig{Name: slot.Name, MType: slot.MType} + for _, iface := range d.methodRefs[sig] { + if impls[iface] == d.ifaceMethodCounts[iface] { + key := ifaceMethodKey{iface: iface, sig: sig} + d.methodImplKeys[id] = append(d.methodImplKeys[id], key) } } } @@ -175,7 +200,11 @@ func (d *pass) flood() { if _, used := d.usedInIface[sym]; used { if _, processed := d.processedIfaceTy[sym]; !processed { d.processedIfaceTy[sym] = struct{}{} - for slot, slotInfo := range d.info.MethodSlots(sym) { + slots := d.info.MethodSlots(sym) + if len(slots) > 0 { + d.computeMethodImplKeys(sym, slots) + } + for slot, slotInfo := range slots { d.markableMethods = append(d.markableMethods, methodRef{ owner: sym, slot: slot, @@ -259,10 +288,16 @@ func (d *pass) markUsedInIface(typ meta.Symbol) { } func (d *pass) markTypeUsedInIface(sym meta.Symbol) { - if _, ok := d.typeSymbols[sym]; !ok { + if _, ok := d.typeSymbols[sym]; ok { + d.markUsedInIface(sym) return } - d.markUsedInIface(sym) + // Lazy check: interfaces are in typeSymbols, concrete types are detected + // by the presence of MethodSlots. + if len(d.info.MethodSlots(sym)) > 0 { + d.typeSymbols[sym] = struct{}{} + d.markUsedInIface(sym) + } } func (d *pass) popWork() meta.Symbol { diff --git a/internal/meta/format.go b/internal/meta/format.go index c676a93bf1..ab1fe627d4 100644 --- a/internal/meta/format.go +++ b/internal/meta/format.go @@ -6,22 +6,22 @@ import ( "strings" ) -// MetaString formats pm as a human-readable string for testing and debugging. -func MetaString(pm *PackageMeta) string { +// String returns a human-readable representation for testing and debugging. +func (pm *PackageMeta) String() string { if pm == nil { return "" } var sb strings.Builder - FormatMeta(&sb, pm) + formatMeta(&sb, pm) return sb.String() } -// FormatMeta writes a human-readable representation of pm to w, +// formatMeta writes a human-readable representation of pm to w, // grouped by section (TypeChildren, OrdinaryEdges, UseIface, etc.) // to match the original metadata format used by golden file tests. -func FormatMeta(w *strings.Builder, pm *PackageMeta) { - n := pm.NSyms() - symName := func(sym LocalSymbol) string { return pm.SymbolName(sym) } +func formatMeta(w *strings.Builder, pm *PackageMeta) { + n := pm.nsyms + symName := func(sym LocalSymbol) string { return pm.symbolName(sym) } // collect per-sym edge lists by kind type kindMap = map[string][]string // src → []dst (sorted) @@ -32,7 +32,7 @@ func FormatMeta(w *strings.Builder, pm *PackageMeta) { for i := LocalSymbol(0); i < LocalSymbol(n); i++ { src := symName(i) - for _, e := range pm.Edges(i) { + for _, e := range pm.edges(i) { switch e.Kind { case EdgeOrdinary: ordinary[src] = append(ordinary[src], symName(LocalSymbol(e.Target))) @@ -41,17 +41,17 @@ func FormatMeta(w *strings.Builder, pm *PackageMeta) { case EdgeUseIfaceMethod: ifaceSym := LocalSymbol(e.Target) iface := symName(ifaceSym) - sigs := pm.IfaceMethods(ifaceSym) + sigs := pm.ifaceMethods(ifaceSym) if int(e.Extra) < len(sigs) { s := sigs[e.Extra] useIfaceMethod[src] = append(useIfaceMethod[src], - fmt.Sprintf("%s %s %s", iface, pm.NameString(s.Name), symName(s.MType))) + fmt.Sprintf("%s %s %s", iface, pm.nameString(s.Name), symName(s.MType))) } else { useIfaceMethod[src] = append(useIfaceMethod[src], fmt.Sprintf("%s[%d]", iface, e.Extra)) } case EdgeUseNamedMethod: - name := pm.NameString(NameRef{Off: e.Target, Len: e.Extra}) + name := pm.nameString(NameRef{Off: e.Target, Len: e.Extra}) useNamed[src] = append(useNamed[src], name) } } @@ -61,7 +61,7 @@ func FormatMeta(w *strings.Builder, pm *PackageMeta) { typeChildren := make(map[string][]string) for i := LocalSymbol(0); i < LocalSymbol(n); i++ { parent := symName(i) - for _, c := range pm.TypeChildren(i) { + for _, c := range pm.typeChildren(i) { typeChildren[parent] = append(typeChildren[parent], symName(c)) } } @@ -71,9 +71,9 @@ func FormatMeta(w *strings.Builder, pm *PackageMeta) { methodInfo := make(map[string][]slotInfo) for i := LocalSymbol(0); i < LocalSymbol(n); i++ { typ := symName(i) - for _, s := range pm.MethodSlots(i) { + for _, s := range pm.methodSlots(i) { methodInfo[typ] = append(methodInfo[typ], slotInfo{ - name: pm.NameString(s.Name), + name: pm.nameString(s.Name), mtype: symName(s.MType), ifn: symName(s.IFn), tfn: symName(s.TFn), @@ -86,9 +86,9 @@ func FormatMeta(w *strings.Builder, pm *PackageMeta) { ifaceInfo := make(map[string][]sigInfo) for i := LocalSymbol(0); i < LocalSymbol(n); i++ { iface := symName(i) - for _, s := range pm.IfaceMethods(i) { + for _, s := range pm.ifaceMethods(i) { ifaceInfo[iface] = append(ifaceInfo[iface], sigInfo{ - name: pm.NameString(s.Name), + name: pm.nameString(s.Name), mtype: symName(s.MType), }) } @@ -97,7 +97,7 @@ func FormatMeta(w *strings.Builder, pm *PackageMeta) { // collect Reflect var reflectSyms []string for i := LocalSymbol(0); i < LocalSymbol(n); i++ { - if pm.HasReflect(i) { + if pm.hasReflect(i) { reflectSyms = append(reflectSyms, symName(i)) } } diff --git a/internal/meta/global.go b/internal/meta/global.go index fd41e6d220..f76ce9b6c4 100644 --- a/internal/meta/global.go +++ b/internal/meta/global.go @@ -25,7 +25,6 @@ type GlobalSummary struct { nameStrings []string // Name → text // per-type flags set at merge time (no translation, just CSR range checks) - isConcrete []bool // global Symbol → true if has method slots isInterface []bool // global Symbol → true if has iface methods reflect map[Symbol]struct{} @@ -33,8 +32,7 @@ type GlobalSummary struct { methodInfo map[Symbol][]GMethodSlot interfaceInfo map[Symbol][]GMethodSig - interfaces []Symbol - concreteTypes []Symbol + interfaces []Symbol } // Symbol is a whole-program symbol ID in GlobalSummary's unified namespace. @@ -96,25 +94,21 @@ func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { if pm == nil { continue } - n := pm.NSyms() + n := pm.nsyms tab := make([]Symbol, n) for li := LocalSymbol(0); li < LocalSymbol(n); li++ { - gs := g.internSymbol(pm.SymbolName(li)) + gs := g.internSymbol(pm.symbolName(li)) tab[li] = gs if g.owner[gs].pkg < 0 && hasFacts(pm, li) { g.owner[gs] = symLoc{pkg: int32(pi), local: li} } // mark type kinds (no translation, just CSR range checks) - if pm.IsConcreteType(li) && !g.isConcrete[gs] { - g.isConcrete[gs] = true - g.concreteTypes = append(g.concreteTypes, gs) - } - if pm.IsInterface(li) && !g.isInterface[gs] { + if pm.nifaceMethod(li) > 0 && !g.isInterface[gs] { g.isInterface[gs] = true g.interfaces = append(g.interfaces, gs) } - if pm.HasReflect(li) { + if pm.hasReflect(li) { g.reflect[gs] = struct{}{} } } @@ -126,11 +120,11 @@ func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { // hasFacts reports whether li carries any facts in pm (i.e. is defined here, // not merely referenced). Used to pick the owning package for lazy queries. func hasFacts(pm *PackageMeta, li LocalSymbol) bool { - return pm.HasEdges(li) || - pm.IsCompositeType(li) || - pm.IsConcreteType(li) || - pm.IsInterface(li) || - pm.HasReflect(li) + return pm.hasEdges(li) || + pm.ntypeChild(li) > 0 || + pm.hasReflect(li) || + pm.nmethodSlot(li) > 0 || + pm.nifaceMethod(li) > 0 } func (g *GlobalSummary) internSymbol(s string) Symbol { @@ -141,7 +135,6 @@ func (g *GlobalSummary) internSymbol(s string) Symbol { g.symIntern[s] = id g.symStrings = append(g.symStrings, s) g.owner = append(g.owner, symLoc{pkg: -1}) - g.isConcrete = append(g.isConcrete, false) g.isInterface = append(g.isInterface, false) return id } @@ -169,11 +162,11 @@ func (g *GlobalSummary) ownerData(sym Symbol) (*PackageMeta, []Symbol, LocalSymb } func (g *GlobalSummary) translateSlots(tab []Symbol, pm *PackageMeta, li LocalSymbol) []GMethodSlot { - local := pm.MethodSlots(li) + local := pm.methodSlots(li) out := make([]GMethodSlot, len(local)) for i, s := range local { out[i] = GMethodSlot{ - Name: g.internName(pm.NameString(s.Name)), + Name: g.internName(pm.nameString(s.Name)), MType: tab[s.MType], IFn: tab[s.IFn], TFn: tab[s.TFn], @@ -183,11 +176,11 @@ func (g *GlobalSummary) translateSlots(tab []Symbol, pm *PackageMeta, li LocalSy } func (g *GlobalSummary) translateSigs(tab []Symbol, pm *PackageMeta, li LocalSymbol) []GMethodSig { - local := pm.IfaceMethods(li) + local := pm.ifaceMethods(li) out := make([]GMethodSig, len(local)) for i, s := range local { out[i] = GMethodSig{ - Name: g.internName(pm.NameString(s.Name)), + Name: g.internName(pm.nameString(s.Name)), MType: tab[s.MType], } } @@ -223,9 +216,6 @@ func (g *GlobalSummary) Name(n Name) string { // Interfaces returns all interface type symbols. func (g *GlobalSummary) Interfaces() []Symbol { return g.interfaces } -// ConcreteTypes returns all concrete type symbols with method slots. -func (g *GlobalSummary) ConcreteTypes() []Symbol { return g.concreteTypes } - // ── lazy per-type queries ───────────────────────────────────────────────────── // MethodSlots returns the ABI method slots for concrete type typ. @@ -277,7 +267,7 @@ func (g *GlobalSummary) ownerEdges(sym Symbol) (*PackageMeta, []Symbol, []Edge) return nil, nil, nil } pm := g.pkgs[loc.pkg] - return pm, g.locToGlb[loc.pkg], pm.Edges(loc.local) + return pm, g.locToGlb[loc.pkg], pm.edges(loc.local) } // OrdinaryEdges returns plain reachability targets from sym (global Symbols). @@ -330,7 +320,7 @@ func (g *GlobalSummary) UseNamedMethod(sym Symbol) []Name { var out []Name for _, e := range edges { if e.Kind == EdgeUseNamedMethod { - name := pm.NameString(NameRef{Off: e.Target, Len: e.Extra}) + name := pm.nameString(NameRef{Off: e.Target, Len: e.Extra}) out = append(out, g.internName(name)) } } @@ -348,7 +338,7 @@ func (g *GlobalSummary) TypeChildren(typ Symbol) []Symbol { } pm := g.pkgs[loc.pkg] tab := g.locToGlb[loc.pkg] - local := pm.TypeChildren(loc.local) + local := pm.typeChildren(loc.local) if len(local) == 0 { return nil } diff --git a/internal/meta/global_test.go b/internal/meta/global_test.go index 58c230f4c3..f8b6b87a1c 100644 --- a/internal/meta/global_test.go +++ b/internal/meta/global_test.go @@ -127,8 +127,8 @@ func TestGlobalSummaryMerge(t *testing.T) { if len(g.Interfaces()) != 1 || g.Interfaces()[0] != reader { t.Errorf("Interfaces() = %v, want [reader=%d]", g.Interfaces(), reader) } - if len(g.ConcreteTypes()) != 1 || g.ConcreteTypes()[0] != myType { - t.Errorf("ConcreteTypes() = %v, want [myType=%d]", g.ConcreteTypes(), myType) + if len(g.MethodSlots(myType)) == 0 { + t.Errorf("MethodSlots(myType) = empty, want non-empty") } } @@ -160,8 +160,8 @@ func TestGlobalSummaryLinkonce(t *testing.T) { foo, _ := g.LookupSymbol("*shared.Foo") // only one MethodInfo entry survives (first-wins), no duplicate concrete type - if got := len(g.ConcreteTypes()); got != 1 { - t.Errorf("ConcreteTypes() len = %d, want 1 (first-wins)", got) + if got := len(g.MethodSlots(foo)); got != 1 { + t.Errorf("MethodSlots(foo) len = %d, want 1 (first-wins)", got) } if got := len(g.MethodSlots(foo)); got != 1 { t.Errorf("MethodSlots(foo) len = %d, want 1", got) diff --git a/internal/meta/meta.go b/internal/meta/meta.go index 7723cda948..45fecb5540 100644 --- a/internal/meta/meta.go +++ b/internal/meta/meta.go @@ -102,12 +102,6 @@ func ReadMeta(path string) (*PackageMeta, error) { return pm, nil } -// NewPackageMetaFromBytes wraps an in-memory byte slice as a PackageMeta. -// The slice must remain valid for the lifetime of the returned PackageMeta. -func NewPackageMetaFromBytes(raw []byte) (*PackageMeta, error) { - return newPackageMeta(raw) -} - // Bytes returns the underlying raw byte slice (for writing to disk). func (pm *PackageMeta) Bytes() []byte { return pm.raw } @@ -121,13 +115,10 @@ func (pm *PackageMeta) Close() error { return nil } -// NSyms returns the number of symbols in this package. -func (pm *PackageMeta) NSyms() uint32 { return pm.nsyms } - -// SymbolName returns the name of sym as a zero-copy view into the string table. +// symbolName returns the name of sym as a zero-copy view into the string table. // The returned string points directly into the mmap region and is only valid for // the lifetime of pm — do not retain it after Close. -func (pm *PackageMeta) SymbolName(sym LocalSymbol) string { +func (pm *PackageMeta) symbolName(sym LocalSymbol) string { if uint32(sym) >= pm.nsyms { return "" } @@ -141,82 +132,60 @@ func (pm *PackageMeta) SymbolName(sym LocalSymbol) string { // NameString returns the string referenced by a NameRef as a zero-copy view // into the string table. The returned string points directly into the mmap // region and is only valid for the lifetime of pm — do not retain it after Close. -func (pm *PackageMeta) NameString(ref NameRef) string { +func (pm *PackageMeta) nameString(ref NameRef) string { return unsafe.String(&pm.raw[pm.strOff+ref.Off], int(ref.Len)) } -// Edges returns all edges from sym as a zero-copy view into the mmap region: -// the on-disk 12-byte edge records are reinterpreted directly as []Edge. The -// result is only valid for the lifetime of pm and must not be mutated. Assumes -// a little-endian host and 4-byte aligned data (guaranteed by stringTable padding). -func (pm *PackageMeta) Edges(sym LocalSymbol) []Edge { - if uint32(sym) >= pm.nsyms { - return nil - } - const recSize = 12 - start, end := pm.csrRange(pm.edgeOff, sym) - if start == end { - return nil - } - dataBase := pm.edgeOff + 4 + (pm.nsyms+1)*4 - p := (*Edge)(unsafe.Pointer(&pm.raw[dataBase+start*recSize])) - return unsafe.Slice(p, end-start) +// NEdge returns the number of outgoing edges from sym, or 0 if none. +func (pm *PackageMeta) nedge(sym LocalSymbol) uint32 { + s, e := pm.csrRange(pm.edgeOff, sym) + return e - s } -// TypeChildren returns the child type LocalSymbols for sym. -// -// The returned slice is a zero-copy view directly into the mmap region: the -// on-disk uint32 array is reinterpreted as []LocalSymbol with no allocation. -// It is only valid for the lifetime of pm (i.e. until Close) and must not be -// mutated. This assumes a little-endian host (matching the wire format) and a -// 4-byte aligned data section (guaranteed by stringTable padding). -func (pm *PackageMeta) TypeChildren(sym LocalSymbol) []LocalSymbol { - if uint32(sym) >= pm.nsyms { - return nil - } - start, end := pm.csrRange(pm.childOff, sym) - if start == end { - return nil - } - dataBase := pm.childOff + 4 + (pm.nsyms+1)*4 - p := (*LocalSymbol)(unsafe.Pointer(&pm.raw[dataBase+start*4])) - return unsafe.Slice(p, end-start) +// Edges returns all edges from sym as a zero-copy view into the mmap region. +func (pm *PackageMeta) edges(sym LocalSymbol) []Edge { + return csrSlice[Edge](pm, pm.edgeOff, sym, 12) +} + +// NTypeChild returns the number of type children for sym, or 0 if none. +func (pm *PackageMeta) ntypeChild(sym LocalSymbol) uint32 { + s, e := pm.csrRange(pm.childOff, sym) + return e - s +} + +// TypeChildren returns the child type LocalSymbols for sym as a zero-copy view +// into the mmap region. +func (pm *PackageMeta) typeChildren(sym LocalSymbol) []LocalSymbol { + return csrSlice[LocalSymbol](pm, pm.childOff, sym, 4) +} + +// NMethodSlot returns the number of ABI method slots for sym, or 0 if none. +func (pm *PackageMeta) nmethodSlot(sym LocalSymbol) uint32 { + s, e := pm.csrRange(pm.methodOff, sym) + return e - s } // MethodSlots returns the ABI method slots for concrete type sym as a zero-copy -// view into the mmap region. Valid only for the lifetime of pm; do not mutate. -func (pm *PackageMeta) MethodSlots(sym LocalSymbol) []MethodSlot { - if uint32(sym) >= pm.nsyms { - return nil - } - const recSize = 20 - start, end := pm.csrRange(pm.methodOff, sym) - if start == end { - return nil - } - dataBase := pm.methodOff + 4 + (pm.nsyms+1)*4 - p := (*MethodSlot)(unsafe.Pointer(&pm.raw[dataBase+start*recSize])) - return unsafe.Slice(p, end-start) +// view into the mmap region. +func (pm *PackageMeta) methodSlots(sym LocalSymbol) []MethodSlot { + return csrSlice[MethodSlot](pm, pm.methodOff, sym, 20) +} + +// NIfaceMethod returns the number of methods in an interface, or 0 if sym is +// not an interface. +func (pm *PackageMeta) nifaceMethod(sym LocalSymbol) uint32 { + s, e := pm.csrRange(pm.ifaceOff, sym) + return e - s } // IfaceMethods returns the method signatures for interface sym as a zero-copy -// view into the mmap region. Valid only for the lifetime of pm; do not mutate. -func (pm *PackageMeta) IfaceMethods(sym LocalSymbol) []MethodSig { - if uint32(sym) >= pm.nsyms { - return nil - } - const recSize = 12 - start, end := pm.csrRange(pm.ifaceOff, sym) - if start == end { - return nil - } - dataBase := pm.ifaceOff + 4 + (pm.nsyms+1)*4 - p := (*MethodSig)(unsafe.Pointer(&pm.raw[dataBase+start*recSize])) - return unsafe.Slice(p, end-start) +// view into the mmap region. +func (pm *PackageMeta) ifaceMethods(sym LocalSymbol) []MethodSig { + return csrSlice[MethodSig](pm, pm.ifaceOff, sym, 12) } // HasReflect reports whether sym triggers conservative reflection handling. -func (pm *PackageMeta) HasReflect(sym LocalSymbol) bool { +func (pm *PackageMeta) hasReflect(sym LocalSymbol) bool { if uint32(sym) >= pm.nsyms { return false } @@ -224,29 +193,11 @@ func (pm *PackageMeta) HasReflect(sym LocalSymbol) bool { return pm.raw[bitmapBase+uint32(sym)/8]&(1<<(sym%8)) != 0 } -// IsCompositeType reports whether sym has TypeChildren (i.e. is a composite type). -func (pm *PackageMeta) IsCompositeType(sym LocalSymbol) bool { - s, e := pm.csrRange(pm.childOff, sym) - return s != e -} - // HasEdges reports whether sym has any outgoing edges. -func (pm *PackageMeta) HasEdges(sym LocalSymbol) bool { - s, e := pm.csrRange(pm.edgeOff, sym) - return s != e -} - -// IsConcreteType reports whether sym has MethodSlots (i.e. is a concrete type with methods). -func (pm *PackageMeta) IsConcreteType(sym LocalSymbol) bool { - s, e := pm.csrRange(pm.methodOff, sym) - return s != e +func (pm *PackageMeta) hasEdges(sym LocalSymbol) bool { + return pm.nedge(sym) > 0 } -// IsInterface reports whether sym has interface methods (i.e. is an interface type). -func (pm *PackageMeta) IsInterface(sym LocalSymbol) bool { - s, e := pm.csrRange(pm.ifaceOff, sym) - return s != e -} // ── internal helpers ────────────────────────────────────────────────────────── @@ -277,8 +228,21 @@ func newPackageMeta(raw []byte) (*PackageMeta, error) { return pm, nil } -// csrRange returns [start, end) data indices for sym in the CSR section -// starting at sectionOff. +// csrSlice returns a zero-copy []T view into a CSR section. recSize is the +// on-disk size of one record (must match unsafe.Sizeof(T)). +func csrSlice[T any](pm *PackageMeta, sectionOff uint32, sym LocalSymbol, recSize uintptr) []T { + if uint32(sym) >= pm.nsyms { + return nil + } + start, end := pm.csrRange(sectionOff, sym) + if start == end { + return nil + } + dataBase := sectionOff + 4 + (pm.nsyms+1)*4 + p := (*T)(unsafe.Pointer(&pm.raw[dataBase+uint32(uintptr(start)*recSize)])) + return unsafe.Slice(p, end-start) +} + func (pm *PackageMeta) csrRange(sectionOff uint32, sym LocalSymbol) (start, end uint32) { offsetsBase := sectionOff + 4 // skip nsyms u32 start = binary.LittleEndian.Uint32(pm.raw[offsetsBase+uint32(sym)*4:]) diff --git a/internal/meta/meta_test.go b/internal/meta/meta_test.go index 9771485f8d..73f8471edc 100644 --- a/internal/meta/meta_test.go +++ b/internal/meta/meta_test.go @@ -1,44 +1,42 @@ -package meta_test +package meta import ( "os" "testing" "unsafe" - - "github.com/goplus/llgo/internal/meta" ) // TestWireLayout verifies the zero-copy structs match their on-disk byte layout: // correct total size and field offsets. If these drift, unsafe reinterpretation // of mmap bytes would silently corrupt — so we assert them explicitly. func TestWireLayout(t *testing.T) { - if got := unsafe.Sizeof(meta.Edge{}); got != 12 { + if got := unsafe.Sizeof(Edge{}); got != 12 { t.Errorf("sizeof(Edge) = %d, want 12", got) } - if got := unsafe.Offsetof(meta.Edge{}.Target); got != 0 { + if got := unsafe.Offsetof(Edge{}.Target); got != 0 { t.Errorf("Edge.Target offset = %d, want 0", got) } - if got := unsafe.Offsetof(meta.Edge{}.Extra); got != 4 { + if got := unsafe.Offsetof(Edge{}.Extra); got != 4 { t.Errorf("Edge.Extra offset = %d, want 4", got) } - if got := unsafe.Offsetof(meta.Edge{}.Kind); got != 8 { + if got := unsafe.Offsetof(Edge{}.Kind); got != 8 { t.Errorf("Edge.Kind offset = %d, want 8", got) } - if got := unsafe.Sizeof(meta.MethodSlot{}); got != 20 { + if got := unsafe.Sizeof(MethodSlot{}); got != 20 { t.Errorf("sizeof(MethodSlot) = %d, want 20", got) } - if got := unsafe.Offsetof(meta.MethodSlot{}.MType); got != 8 { + if got := unsafe.Offsetof(MethodSlot{}.MType); got != 8 { t.Errorf("MethodSlot.MType offset = %d, want 8", got) } - if got := unsafe.Offsetof(meta.MethodSlot{}.TFn); got != 16 { + if got := unsafe.Offsetof(MethodSlot{}.TFn); got != 16 { t.Errorf("MethodSlot.TFn offset = %d, want 16", got) } - if got := unsafe.Sizeof(meta.MethodSig{}); got != 12 { + if got := unsafe.Sizeof(MethodSig{}); got != 12 { t.Errorf("sizeof(MethodSig) = %d, want 12", got) } - if got := unsafe.Offsetof(meta.MethodSig{}.MType); got != 8 { + if got := unsafe.Offsetof(MethodSig{}.MType); got != 8 { t.Errorf("MethodSig.MType offset = %d, want 8", got) } } @@ -48,7 +46,7 @@ func TestWireLayout(t *testing.T) { // that stringTable padding keeps the zero-copy TypeChildren view correctly aligned. func TestTypeChildrenAlignment(t *testing.T) { for _, pad := range []string{"a", "ab", "abc", "abcd", "abcde"} { - b := meta.NewBuilder() + b := NewBuilder() // a symbol whose name length varies, to shift the string table size b.Sym("x." + pad) parent := b.Sym("*pkg.Parent") @@ -63,8 +61,8 @@ func TestTypeChildrenAlignment(t *testing.T) { if err != nil { t.Fatalf("pad=%q build: %v", pad, err) } - got := pm.TypeChildren(parent) - want := []meta.LocalSymbol{c0, c1, c2} + got := pm.typeChildren(parent) + want := []LocalSymbol{c0, c1, c2} if len(got) != len(want) { t.Fatalf("pad=%q TypeChildren len = %d, want %d", pad, len(got), len(want)) } @@ -79,7 +77,7 @@ func TestTypeChildrenAlignment(t *testing.T) { // TestRoundTrip builds a small package summary, serializes it, then reads it // back and verifies every query returns the expected values. func TestRoundTrip(t *testing.T) { - b := meta.NewBuilder() + b := NewBuilder() // symbols main := b.Sym("main.main") @@ -93,14 +91,14 @@ func TestRoundTrip(t *testing.T) { tfn := b.Sym("(*MyStruct).Read$tfn") // ordinary edges - b.AddEdge(main, helper, meta.EdgeOrdinary, 0) - b.AddEdge(main, allocZ, meta.EdgeOrdinary, 0) + b.AddEdge(main, helper, EdgeOrdinary, 0) + b.AddEdge(main, allocZ, EdgeOrdinary, 0) // interface conversion - b.AddEdge(main, myType, meta.EdgeUseIface, 0) + b.AddEdge(main, myType, EdgeUseIface, 0) // interface method call: Reader.Read is method index 0 - b.AddEdge(main, myIface, meta.EdgeUseIfaceMethod, 0) + b.AddEdge(main, myIface, EdgeUseIfaceMethod, 0) // named method call b.AddNamedMethodEdge(helper, "ServeHTTP") @@ -125,9 +123,9 @@ func TestRoundTrip(t *testing.T) { // ── verify Symbols ──────────────────────────────────────────────────────── - checkName := func(sym meta.LocalSymbol, want string) { + checkName := func(sym LocalSymbol, want string) { t.Helper() - if got := pm.SymbolName(sym); got != want { + if got := pm.symbolName(sym); got != want { t.Errorf("SymbolName(%d) = %q, want %q", sym, got, want) } } @@ -138,104 +136,104 @@ func TestRoundTrip(t *testing.T) { // ── verify Edges ────────────────────────────────────────────────────────── - mainEdges := pm.Edges(main) + mainEdges := pm.edges(main) if len(mainEdges) != 4 { t.Fatalf("Edges(main): got %d edges, want 4", len(mainEdges)) } - if e := mainEdges[0]; e.Kind != meta.EdgeOrdinary || meta.LocalSymbol(e.Target) != helper { + if e := mainEdges[0]; e.Kind != EdgeOrdinary || LocalSymbol(e.Target) != helper { t.Errorf("edge[0] = %+v, want {Target:%d Kind:Ordinary}", e, helper) } - if e := mainEdges[1]; e.Kind != meta.EdgeOrdinary || meta.LocalSymbol(e.Target) != allocZ { + if e := mainEdges[1]; e.Kind != EdgeOrdinary || LocalSymbol(e.Target) != allocZ { t.Errorf("edge[1] = %+v, want {Target:%d Kind:Ordinary}", e, allocZ) } - if e := mainEdges[2]; e.Kind != meta.EdgeUseIface || meta.LocalSymbol(e.Target) != myType { + if e := mainEdges[2]; e.Kind != EdgeUseIface || LocalSymbol(e.Target) != myType { t.Errorf("edge[2] = %+v, want {Target:%d Kind:UseIface}", e, myType) } - if e := mainEdges[3]; e.Kind != meta.EdgeUseIfaceMethod || meta.LocalSymbol(e.Target) != myIface || e.Extra != 0 { + if e := mainEdges[3]; e.Kind != EdgeUseIfaceMethod || LocalSymbol(e.Target) != myIface || e.Extra != 0 { t.Errorf("edge[3] = %+v, want {Target:%d Kind:UseIfaceMethod Extra:0}", e, myIface) } - helperEdges := pm.Edges(helper) + helperEdges := pm.edges(helper) if len(helperEdges) != 1 { t.Fatalf("Edges(helper): got %d, want 1", len(helperEdges)) } - if e := helperEdges[0]; e.Kind != meta.EdgeUseNamedMethod { + if e := helperEdges[0]; e.Kind != EdgeUseNamedMethod { t.Errorf("helper edge[0].Kind = %d, want UseNamedMethod", e.Kind) } // For UseNamedMethod, target=NameRef.Off and extra=NameRef.Len. - gotName := pm.NameString(meta.NameRef{Off: helperEdges[0].Target, Len: helperEdges[0].Extra}) + gotName := pm.nameString(NameRef{Off: helperEdges[0].Target, Len: helperEdges[0].Extra}) if gotName != "ServeHTTP" { t.Errorf("UseNamedMethod target name = %q, want \"ServeHTTP\"", gotName) } - if got := pm.Edges(allocZ); len(got) != 0 { + if got := pm.edges(allocZ); len(got) != 0 { t.Errorf("Edges(allocZ): got %d, want 0", len(got)) } // ── verify TypeChildren ─────────────────────────────────────────────────── - children := pm.TypeChildren(myType) + children := pm.typeChildren(myType) if len(children) != 1 || children[0] != myField { t.Errorf("TypeChildren(myType) = %v, want [%d]", children, myField) } - if pm.TypeChildren(main) != nil { + if pm.typeChildren(main) != nil { t.Errorf("TypeChildren(main) should be nil") } - if !pm.IsCompositeType(myType) { - t.Errorf("IsCompositeType(myType) = false, want true") + if pm.ntypeChild(myType) == 0 { + t.Errorf("NTypeChild(myType) = 0, want >0") } - if pm.IsCompositeType(main) { - t.Errorf("IsCompositeType(main) = true, want false") + if pm.ntypeChild(main) > 0 { + t.Errorf("NTypeChild(main) > 0, want 0") } // ── verify MethodSlots ──────────────────────────────────────────────────── - slots := pm.MethodSlots(myType) + slots := pm.methodSlots(myType) if len(slots) != 1 { t.Fatalf("MethodSlots(myType): got %d, want 1", len(slots)) } slot := slots[0] - if pm.NameString(slot.Name) != "Read" { - t.Errorf("slot.Name = %q, want \"Read\"", pm.NameString(slot.Name)) + if pm.nameString(slot.Name) != "Read" { + t.Errorf("slot.Name = %q, want \"Read\"", pm.nameString(slot.Name)) } if slot.MType != mtype || slot.IFn != ifn || slot.TFn != tfn { t.Errorf("slot = %+v, unexpected symbols", slot) } - if !pm.IsConcreteType(myType) { - t.Errorf("IsConcreteType(myType) = false, want true") + if len(pm.methodSlots(myType)) == 0 { + t.Errorf("MethodSlots(myType) = empty, want non-empty") } // ── verify IfaceMethods ─────────────────────────────────────────────────── - sigs := pm.IfaceMethods(myIface) + sigs := pm.ifaceMethods(myIface) if len(sigs) != 1 { t.Fatalf("IfaceMethods(myIface): got %d, want 1", len(sigs)) } - if pm.NameString(sigs[0].Name) != "Read" { - t.Errorf("iface method name = %q, want \"Read\"", pm.NameString(sigs[0].Name)) + if pm.nameString(sigs[0].Name) != "Read" { + t.Errorf("iface method name = %q, want \"Read\"", pm.nameString(sigs[0].Name)) } - if !pm.IsInterface(myIface) { - t.Errorf("IsInterface(myIface) = false, want true") + if pm.nifaceMethod(myIface) == 0 { + t.Errorf("NIfaceMethod(myIface) = 0, want >0") } - if pm.IsInterface(main) { - t.Errorf("IsInterface(main) = true, want false") + if pm.nifaceMethod(main) > 0 { + t.Errorf("NIfaceMethod(main) > 0, want 0") } // ── verify ReflectBitmap ────────────────────────────────────────────────── - if !pm.HasReflect(helper) { + if !pm.hasReflect(helper) { t.Errorf("HasReflect(helper) = false, want true") } - if pm.HasReflect(main) { + if pm.hasReflect(main) { t.Errorf("HasReflect(main) = true, want false") } } // TestRoundTripFile writes the meta to disk and reads it back via ReadMeta. func TestRoundTripFile(t *testing.T) { - b := meta.NewBuilder() + b := NewBuilder() fn := b.Sym("pkg.Fn") dep := b.Sym("runtime.X") - b.AddEdge(fn, dep, meta.EdgeOrdinary, 0) + b.AddEdge(fn, dep, EdgeOrdinary, 0) pm, err := b.Build() if err != nil { @@ -247,17 +245,17 @@ func TestRoundTripFile(t *testing.T) { t.Fatalf("write: %v", err) } - pm2, err := meta.ReadMeta(path) + pm2, err := ReadMeta(path) if err != nil { t.Fatalf("ReadMeta: %v", err) } defer pm2.Close() - if got := pm2.SymbolName(fn); got != "pkg.Fn" { + if got := pm2.symbolName(fn); got != "pkg.Fn" { t.Errorf("SymbolName after file round-trip = %q, want \"pkg.Fn\"", got) } - edges := pm2.Edges(fn) - if len(edges) != 1 || meta.LocalSymbol(edges[0].Target) != dep { + edges := pm2.edges(fn) + if len(edges) != 1 || LocalSymbol(edges[0].Target) != dep { t.Errorf("Edges after file round-trip = %v", edges) } } From 1542da561cf781da08c2510eec534837184bf4a9 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sat, 27 Jun 2026 22:31:13 +0800 Subject: [PATCH 19/37] style: go fmt --- internal/deadcode/analyze.go | 1 - internal/meta/global.go | 2 +- internal/meta/meta.go | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/deadcode/analyze.go b/internal/deadcode/analyze.go index 1fed7a958e..77a75eb281 100644 --- a/internal/deadcode/analyze.go +++ b/internal/deadcode/analyze.go @@ -315,4 +315,3 @@ func appendSymbolUnique(items []meta.Symbol, item meta.Symbol) []meta.Symbol { } return append(items, item) } - diff --git a/internal/meta/global.go b/internal/meta/global.go index f76ce9b6c4..c7d1fef666 100644 --- a/internal/meta/global.go +++ b/internal/meta/global.go @@ -16,7 +16,7 @@ type GlobalSummary struct { // symbol space symIntern map[string]Symbol - symStrings []string // Symbol → text + symStrings []string // Symbol → text locToGlb [][]Symbol // [pkgIdx][localSym] → global Symbol owner []symLoc // global Symbol → owning (pkg, local); pkg<0 if none diff --git a/internal/meta/meta.go b/internal/meta/meta.go index 45fecb5540..6319b5b15d 100644 --- a/internal/meta/meta.go +++ b/internal/meta/meta.go @@ -198,7 +198,6 @@ func (pm *PackageMeta) hasEdges(sym LocalSymbol) bool { return pm.nedge(sym) > 0 } - // ── internal helpers ────────────────────────────────────────────────────────── // newPackageMeta parses the header of raw and returns a PackageMeta. From 38b515419fee0ac536f8a269c4c02f08eafc2748 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sat, 27 Jun 2026 22:31:39 +0800 Subject: [PATCH 20/37] style: go fmt collect_test.go --- internal/build/collect_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/build/collect_test.go b/internal/build/collect_test.go index d308a3599b..23022df3d3 100644 --- a/internal/build/collect_test.go +++ b/internal/build/collect_test.go @@ -535,7 +535,7 @@ func TestSaveToCache_Success(t *testing.T) { return m.Build() }(), ObjFiles: []string{objFile.Name()}, - Meta: func() *meta.PackageMeta { pm, _ := meta.NewBuilder().Build(); return pm }(), + Meta: func() *meta.PackageMeta { pm, _ := meta.NewBuilder().Build(); return pm }(), } if err := ctx.saveToCache(pkg); err != nil { From 7fd02e9c217c772c1bb10d4f022cf67233471f58 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jun 2026 11:53:33 +0800 Subject: [PATCH 21/37] fix(ssa): emit interface metadata from type descriptors --- .../interface_exported_var/meta-expect.txt | 5 +-- cl/_testmeta/interface_named/meta-expect.txt | 4 +- .../interface_unexported/meta-expect.txt | 4 +- .../methodinfo_imported/meta-expect.txt | 22 +++++++--- cl/_testmeta/reflect_named/meta-expect.txt | 44 +------------------ ssa/abitype.go | 19 +++++--- ssa/interface.go | 10 +---- 7 files changed, 39 insertions(+), 69 deletions(-) diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt index b7237af82b..cb2a43e790 100644 --- a/cl/_testmeta/interface_exported_var/meta-expect.txt +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -306,7 +306,6 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: encoding/binary.LittleEndian github.com/goplus/llgo/runtime/internal/runtime.AllocU github.com/goplus/llgo/runtime/internal/runtime.AllocZ - github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData github.com/goplus/llgo/runtime/internal/runtime.NewItab github.com/goplus/llgo/runtime/internal/runtime.Typedmemmove @@ -317,7 +316,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: - _llgo_encoding/binary.ByteOrder Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_encoding/binary.ByteOrder[4] [MethodInfo] *_llgo_encoding/binary.littleEndian: @@ -346,7 +345,7 @@ _llgo_encoding/binary.littleEndian: 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 [InterfaceInfo] -_llgo_encoding/binary.ByteOrder: +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index c9133a82ab..f20e8893c9 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_named.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I[0] [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: @@ -69,6 +69,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M [InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index 0e8d32cc9c..00b6bb5729 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I[0] [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: @@ -69,6 +69,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m [InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index 736c5e6778..649cebb076 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -319,7 +319,9 @@ _llgo_bytes.readOp: _llgo_error: *_llgo_error __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - _llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods + _llgo_error$imethods +_llgo_error$imethods: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: @@ -461,10 +463,6 @@ _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: _llgo_string -_llgo_iface$Fh8eUJ-Gw4e6TYuajcFIOSCuqSPKAt5nS4ow7xeGXEU$imethods: - _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to -_llgo_iface$kr1iSWwMezh0B9LdQN0MhEZUNZvBlHPhlst95jAyxE0$imethods: - _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: *_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal @@ -483,11 +481,15 @@ _llgo_int64: _llgo_io.Reader: *_llgo_io.Reader __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods + _llgo_io.Reader$imethods +_llgo_io.Reader$imethods: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Writer: *_llgo_io.Writer __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - _llgo_iface$kr1iSWwMezh0B9LdQN0MhEZUNZvBlHPhlst95jAyxE0$imethods + _llgo_io.Writer$imethods +_llgo_io.Writer$imethods: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_string: *_llgo_string __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal @@ -548,6 +550,12 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: 26 bytes.tryGrowByReslice _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M bytes.(*Buffer).tryGrowByReslice __llgo_stub.bytes.(*Buffer).tryGrowByReslice [InterfaceInfo] +_llgo_error: + Error _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Reader: Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +_llgo_io.Writer: + Write _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index 187a7c38b4..54e2cbf315 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -52,8 +52,8 @@ github.com/goplus/llgo/cl/_testmeta/reflect_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: - _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E - _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + _llgo_reflect.Type[21] + _llgo_reflect.Type[21] [UseNamedMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: @@ -68,43 +68,3 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m -[InterfaceInfo] -_llgo_reflect.Type: - Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - AssignableTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE - Bits _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - CanSeq _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - CanSeq2 _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - ChanDir _llgo_func$JO3khPIbANSMBmoN6P7ybYAeUBd3Gv6toVUqNeE7qbE - Comparable _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - ConvertibleTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE - Elem _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI - Field _llgo_func$Q3NYrysaKgu1MtMuLQwb-k5QcKGHihnt-tV_NlNJQFA - FieldAlign _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - FieldByIndex _llgo_func$LPPtiM49dEPl48CC3WRhXm3YPnfUJEZE_k8Tx3rMuSk - FieldByName _llgo_func$dEvABJ5r0MMUlf4smWpIDG5dO8AuGklGdNJ1xneL3UM - FieldByNameFunc _llgo_func$xySrXVFC_2LK2oP71R2UryKi6UmdEJUo9k6aQuz4TvI - Implements _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE - In _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY - IsVariadic _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - Key _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI - Kind _llgo_func$w8Mj2LK8G5p7MIiGWR6MYjyXy3L8SVVzYlT1bb6KNXk - Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - Method _llgo_func$FmJJGomlX5kINJGxQdQDCAkD89ySoMslAYFrziWInVc - MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E - Name _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - NumField _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - NumIn _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - NumMethod _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - NumOut _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - Out _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY - OverflowComplex _llgo_func$cGkbH-2LQOLoq64Rqj3WeO56U8al7FfVkf5K1FFbPpE - OverflowFloat _llgo_func$uk7PgUVap9GZdvS8R_mZCDbAbqnAbcNryqybtDogUNI - OverflowInt _llgo_func$odFOIClZoEVGbTP_BEfZxVM5ex3r8Fj1afUEeP_awp8 - OverflowUint _llgo_func$7I97sofX8UqJA96mVIy89KPUfSM_efkrR-mJQ9qaHfk - PkgPath _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - Size _llgo_func$1kITCsyu7hFLMxHLR7kDlvu4SOra_HtrtdFUQH9P13s - String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - reflect.common _llgo_func$w6XuV-1SmW103DbauPseXBpW50HpxXAEsUsGFibl0Uw - reflect.uncommon _llgo_func$iG49bujiXjI2lVflYdE0hPXlCAABL-XKRANSNJEKOio - diff --git a/ssa/abitype.go b/ssa/abitype.go index d78ec2d548..71c8f8a88d 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -199,13 +199,23 @@ type Imethod struct { } */ -func (b Builder) abiInterfaceImethods(t *types.Interface, name string) llvm.Value { +func (b Builder) abiInterfaceImethods(t *types.Interface, typeName string) llvm.Value { prog := b.Prog n := t.NumMethods() if n == 0 { return prog.Nil(prog.rtType("Slice")).impl } - g := b.Pkg.VarOf(name) + if mb := b.Pkg.MetaBuilder; mb != nil { + intfSym := mb.Sym(typeName) + for i := 0; i < n; i++ { + f := t.Method(i) + ftypName, _ := prog.abi.TypeName(funcType(prog, f.Type())) + mb.AddIfaceMethod(intfSym, mthName(f), mb.Sym(ftypName)) + } + } + + imethodsName := typeName + "$imethods" + g := b.Pkg.VarOf(imethodsName) if g == nil { ft := prog.rtType("Imethod") fields := make([]llvm.Value, n) @@ -223,7 +233,7 @@ func (b Builder) abiInterfaceImethods(t *types.Interface, name string) llvm.Valu } atyp := prog.rawType(types.NewArray(ft.RawType(), int64(n))) data := Expr{llvm.ConstArray(ft.ll, fields), atyp} - g = b.Pkg.doNewVar(name, prog.Pointer(atyp)) + g = b.Pkg.doNewVar(imethodsName, prog.Pointer(atyp)) g.Init(data) g.impl.SetGlobalConstant(true) g.impl.SetLinkage(llvm.WeakODRLinkage) @@ -330,10 +340,9 @@ func (b Builder) abiExtendedFields(t types.Type, name string, global llvm.Value) b.abiStructFields(t, name+"$fields"), } case *types.Interface: - name, _ = prog.abi.TypeName(t) fields = []llvm.Value{ b.Str(pkg.Path()).impl, - b.abiInterfaceImethods(t, name+"$imethods"), + b.abiInterfaceImethods(t, name), } case *types.Named: return b.abiExtendedFields(t.Underlying(), name, global) diff --git a/ssa/interface.go b/ssa/interface.go index 579efd721f..42bc9de822 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -83,15 +83,9 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { i := iMethodOf(rawIntf, method.Name()) if mb := b.Pkg.MetaBuilder; mb != nil { intfSym := mb.Sym(func() string { n, _ := prog.abi.TypeName(intf.raw.Type); return n }()) - // Record which interface method is demanded. i is the method's index in - // rawIntf (same order we register methods below), so it becomes extra. + // Record which interface method is demanded. i is the method's index in rawIntf. + // Interface method sets are emitted when the interface type descriptor is built. mb.AddEdge(mb.Sym(b.Func.Name()), intfSym, meta.EdgeUseIfaceMethod, uint32(i)) - // Register all interface methods — AddIfaceMethod is idempotent per (name, mtype). - for j := 0; j < rawIntf.NumMethods(); j++ { - im := rawIntf.Method(j) - imtypeName, _ := prog.abi.TypeName(funcType(prog, im.Type())) - mb.AddIfaceMethod(intfSym, mthName(im), mb.Sym(imtypeName)) - } } data := b.InlineCall(b.Pkg.rtFunc("IfacePtrData"), intf) var fn Expr From 55a77fe343955147b1c2b5aaf2bff10f18f6cfa6 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jun 2026 12:38:05 +0800 Subject: [PATCH 22/37] fix(ssa): align named interface metadata --- .../interface_exported_var/meta-expect.txt | 56 +++++++++---------- cl/_testmeta/interface_named/meta-expect.txt | 30 +++++----- .../interface_unexported/meta-expect.txt | 30 +++++----- .../methodinfo_imported/meta-expect.txt | 17 +----- internal/deadcode/analyze.go | 7 --- ssa/interface.go | 23 ++++---- 6 files changed, 70 insertions(+), 93 deletions(-) diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt index cb2a43e790..e5e6992190 100644 --- a/cl/_testmeta/interface_exported_var/meta-expect.txt +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -1,6 +1,8 @@ [TypeChildren] *[]_llgo_uint8: []_llgo_uint8 +*_llgo_encoding/binary.ByteOrder: + _llgo_encoding/binary.ByteOrder *_llgo_encoding/binary.littleEndian: _llgo_encoding/binary.littleEndian *_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: @@ -23,8 +25,6 @@ _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to -*_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: - _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw *_llgo_string: _llgo_string *_llgo_uint16: @@ -37,6 +37,14 @@ _llgo_uint8 []_llgo_uint8: _llgo_uint8 +_llgo_encoding/binary.ByteOrder: + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: []_llgo_uint8 _llgo_uint64 @@ -66,19 +74,14 @@ _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: _llgo_uint64 _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_string -_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: - _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc - _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg - _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus - _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU - _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 - _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE - _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to [OrdinaryEdges] *[]_llgo_uint8: []_llgo_uint8 __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr +*_llgo_encoding/binary.ByteOrder: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_encoding/binary.ByteOrder *_llgo_encoding/binary.littleEndian: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_encoding/binary.littleEndian @@ -112,9 +115,6 @@ _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to -*_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr - _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw *_llgo_string: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_string @@ -193,6 +193,18 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: github.com/goplus/llgo/runtime/internal/runtime.memequalptr __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_encoding/binary.ByteOrder: + *_llgo_encoding/binary.ByteOrder + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_encoding/binary.ByteOrder$imethods +_llgo_encoding/binary.ByteOrder$imethods: + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_encoding/binary.littleEndian: *_llgo_encoding/binary.littleEndian __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 @@ -270,18 +282,6 @@ _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: _llgo_string -_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: - *_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods -_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods: - _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc - _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg - _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus - _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU - _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 - _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE - _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_string: *_llgo_string __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal @@ -301,8 +301,8 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init: encoding/binary.init github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init$guard github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: + _llgo_encoding/binary.ByteOrder _llgo_encoding/binary.littleEndian - _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw encoding/binary.LittleEndian github.com/goplus/llgo/runtime/internal/runtime.AllocU github.com/goplus/llgo/runtime/internal/runtime.AllocZ @@ -316,7 +316,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: - _llgo_encoding/binary.ByteOrder[4] + _llgo_encoding/binary.ByteOrder Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus [MethodInfo] *_llgo_encoding/binary.littleEndian: @@ -345,7 +345,7 @@ _llgo_encoding/binary.littleEndian: 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 [InterfaceInfo] -_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: +_llgo_encoding/binary.ByteOrder: PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index f20e8893c9..6a319f3b41 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -1,23 +1,23 @@ [TypeChildren] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T -*_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: - _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 -_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [OrdinaryEdges] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T -*_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr - _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M: @@ -30,15 +30,15 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I$imethods +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 -_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: - *_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods -_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods: - _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: github.com/goplus/llgo/cl/_testmeta/interface_named.T.M github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref @@ -46,8 +46,8 @@ github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: github.com/goplus/llgo/cl/_testmeta/interface_named.init: github.com/goplus/llgo/cl/_testmeta/interface_named.init$guard github.com/goplus/llgo/cl/_testmeta/interface_named.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T - _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 github.com/goplus/llgo/cl/_testmeta/interface_named.use github.com/goplus/llgo/runtime/internal/runtime.AllocU github.com/goplus/llgo/runtime/internal/runtime.NewItab @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_named.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I[0] + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: @@ -69,6 +69,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M [InterfaceInfo] -_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index 00b6bb5729..a095899883 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -1,23 +1,23 @@ [TypeChildren] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T -*github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: - github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo -github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [OrdinaryEdges] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T -*github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr - github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m: @@ -30,6 +30,12 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I$imethods +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 @@ -37,17 +43,11 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer -github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: - *github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods -github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods: - _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.init: github.com/goplus/llgo/cl/_testmeta/interface_unexported.init$guard github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T - github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo github.com/goplus/llgo/cl/_testmeta/interface_unexported.use github.com/goplus/llgo/runtime/internal/runtime.AllocU github.com/goplus/llgo/runtime/internal/runtime.NewItab @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I[0] + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: @@ -69,6 +69,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m [InterfaceInfo] -github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index 649cebb076..882c4cba46 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -49,8 +49,6 @@ _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to -*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: - _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw *_llgo_int: _llgo_int *_llgo_int32: @@ -131,8 +129,6 @@ _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: _llgo_uint8 _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_string -_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: - _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Reader: _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Writer: @@ -214,9 +210,6 @@ _llgo_io.Writer: *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to -*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr - _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw *_llgo_int: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_int @@ -463,12 +456,6 @@ _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: _llgo_string -_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: - *_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods -_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods: - _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_int: *_llgo_int __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 @@ -506,7 +493,7 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.init: io.init github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: *_llgo_bytes.Buffer - _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + _llgo_io.Reader bytes.NewBuffer github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData github.com/goplus/llgo/runtime/internal/runtime.NewItab @@ -552,8 +539,6 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: [InterfaceInfo] _llgo_error: Error _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to -_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: - Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Reader: Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Writer: diff --git a/internal/deadcode/analyze.go b/internal/deadcode/analyze.go index 77a75eb281..894ad0fa2a 100644 --- a/internal/deadcode/analyze.go +++ b/internal/deadcode/analyze.go @@ -1,11 +1,8 @@ package deadcode import ( - "fmt" "go/token" - "os" "sort" - "time" "github.com/goplus/llgo/internal/meta" ) @@ -110,7 +107,6 @@ func deadcode(info *meta.GlobalSummary, roots []meta.Symbol) map[meta.Symbol][]i // Concrete type methodImplKeys are NOT computed here. They are built lazily in // computeMethodImplKeys when a type first enters usedInIface. func (d *pass) buildMethodRefs() { - t0 := time.Now() for _, iface := range d.info.Interfaces() { d.typeSymbols[iface] = struct{}{} seenNames := make(map[meta.Name]struct{}) @@ -123,9 +119,6 @@ func (d *pass) buildMethodRefs() { d.ifaceMethodCounts[iface]++ } } - t1 := time.Now() - fmt.Fprintf(os.Stderr, "[dce] methodRefs index: interfaces=%d total_sigs=%d %v\n", - len(d.info.Interfaces()), len(d.methodRefs), t1.Sub(t0)) } // computeMethodImplKeys lazily builds methodImplKeys for a single concrete type diff --git a/ssa/interface.go b/ssa/interface.go index 42bc9de822..a4f31fbc63 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -42,11 +42,12 @@ 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 { +func (b Builder) unsafeInterface(intfType types.Type, t Expr, data llvm.Value) llvm.Value { + rawIntf := intfType.Underlying().(*types.Interface) if rawIntf.Empty() { return b.unsafeEface(t.impl, data) } - tintf := b.abiType(rawIntf) + tintf := b.abiType(intfType) itab := b.newItab(tintf, t) return b.unsafeIface(itab.impl, data) } @@ -135,14 +136,14 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { if !directIfaceType(typ.raw.Type) { vptr := b.AllocU(typ) b.Store(vptr, x) - return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter} + return Expr{b.unsafeInterface(tinter.raw.Type, tabi, vptr.impl), tinter} } kind, _, lvl := abi.DataKindOf(typ.raw.Type, 0, prog.is32Bits) switch kind { case abi.Indirect: vptr := b.AllocU(typ) b.Store(vptr, x) - return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter} + return Expr{b.unsafeInterface(tinter.raw.Type, tabi, vptr.impl), tinter} } ximpl := x.impl if lvl > 0 { @@ -151,7 +152,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { var u llvm.Value switch kind { case abi.Pointer: - return Expr{b.unsafeInterface(rawIntf, tabi, ximpl), tinter} + return Expr{b.unsafeInterface(tinter.raw.Type, tabi, ximpl), tinter} case abi.Integer: tu := prog.Uintptr() u = llvm.CreateIntCast(b.impl, ximpl, tu.ll) @@ -166,11 +167,10 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { panic("todo") } data := llvm.CreateIntToPtr(b.impl, u, prog.tyVoidPtr()) - return Expr{b.unsafeInterface(rawIntf, tabi, data), tinter} + return Expr{b.unsafeInterface(tinter.raw.Type, tabi, data), tinter} } func (b Builder) MakeInterfaceFromPtr(tinter Type, ptr Expr) (ret Expr) { - rawIntf := tinter.raw.Type.Underlying().(*types.Interface) prog := b.Prog b.AssertNilDeref(ptr) @@ -185,7 +185,7 @@ func (b Builder) MakeInterfaceFromPtr(tinter Type, ptr Expr) (ret Expr) { dst := b.Convert(prog.VoidPtr(), vptr) src := b.Convert(prog.VoidPtr(), ptr) b.Call(b.Pkg.rtFunc("Typedmemmove"), tabi, dst, src) - return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter} + return Expr{b.unsafeInterface(tinter.raw.Type, tabi, vptr.impl), tinter} } func (b Builder) recordUseIface(typ Type) { @@ -310,9 +310,9 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) Expr { eq = b.BinOp(token.NEQ, tx, b.Prog.Zero(b.Prog.AbiTypePtr())) val = func() Expr { return x } } else { - if rawIntf, ok := assertedTyp.raw.Type.Underlying().(*types.Interface); ok { + if _, ok := assertedTyp.raw.Type.Underlying().(*types.Interface); ok { eq = b.InlineCall(b.Pkg.rtFunc("Implements"), tabi, tx) - val = func() Expr { return Expr{b.unsafeInterface(rawIntf, tx, b.faceData(x.impl)), assertedTyp} } + val = func() Expr { return Expr{b.unsafeInterface(assertedTyp.raw.Type, tx, b.faceData(x.impl)), assertedTyp} } } else if assertedTyp.kind == vkClosure { eq = b.InlineCall(b.Pkg.rtFunc("MatchesClosure"), tabi, tx) val = func() Expr { return b.valFromData(assertedTyp, b.faceData(x.impl)) } @@ -376,10 +376,9 @@ func typeAssertMissingMethod(assertedTyp Type) string { // // t1 = change interface interface{} <- I (t0) func (b Builder) ChangeInterface(typ Type, x Expr) (ret Expr) { - rawIntf := typ.raw.Type.Underlying().(*types.Interface) tabi := b.faceAbiType(x) data := b.faceData(x.impl) - return Expr{b.unsafeInterface(rawIntf, tabi, data), typ} + return Expr{b.unsafeInterface(typ.raw.Type, tabi, data), typ} } // ----------------------------------------------------------------------------- From 400407f78a64a29a6bf5923d338b294c0dfe61d1 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jun 2026 13:45:04 +0800 Subject: [PATCH 23/37] fix(ssa): keep interface metadata descriptor stable --- .../interface_exported_var/meta-expect.txt | 56 +++++++++---------- cl/_testmeta/interface_named/meta-expect.txt | 30 +++++----- .../interface_unexported/meta-expect.txt | 30 +++++----- .../methodinfo_imported/meta-expect.txt | 19 ++++++- cl/_testmeta/reflect_named/meta-expect.txt | 4 +- ssa/interface.go | 25 +++++---- 6 files changed, 90 insertions(+), 74 deletions(-) diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt index e5e6992190..4be1e14c08 100644 --- a/cl/_testmeta/interface_exported_var/meta-expect.txt +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -1,8 +1,6 @@ [TypeChildren] *[]_llgo_uint8: []_llgo_uint8 -*_llgo_encoding/binary.ByteOrder: - _llgo_encoding/binary.ByteOrder *_llgo_encoding/binary.littleEndian: _llgo_encoding/binary.littleEndian *_llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: @@ -25,6 +23,8 @@ _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw *_llgo_string: _llgo_string *_llgo_uint16: @@ -37,14 +37,6 @@ _llgo_uint8 []_llgo_uint8: _llgo_uint8 -_llgo_encoding/binary.ByteOrder: - _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc - _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg - _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus - _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU - _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 - _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE - _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_func$HQem8FNvPqrEVQ_c0XssBDFXIDOnET_Ex7o3PlV9bSs: []_llgo_uint8 _llgo_uint64 @@ -74,14 +66,19 @@ _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE: _llgo_uint64 _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_string +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to [OrdinaryEdges] *[]_llgo_uint8: []_llgo_uint8 __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr -*_llgo_encoding/binary.ByteOrder: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr - _llgo_encoding/binary.ByteOrder *_llgo_encoding/binary.littleEndian: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_encoding/binary.littleEndian @@ -115,6 +112,9 @@ _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw *_llgo_string: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_string @@ -193,18 +193,6 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: github.com/goplus/llgo/runtime/internal/runtime.memequalptr __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: github.com/goplus/llgo/runtime/internal/runtime.strequal -_llgo_encoding/binary.ByteOrder: - *_llgo_encoding/binary.ByteOrder - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - _llgo_encoding/binary.ByteOrder$imethods -_llgo_encoding/binary.ByteOrder$imethods: - _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc - _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg - _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus - _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU - _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 - _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE - _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_encoding/binary.littleEndian: *_llgo_encoding/binary.littleEndian __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 @@ -282,6 +270,18 @@ _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: _llgo_string +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: + *_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw$imethods: + _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to _llgo_string: *_llgo_string __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal @@ -301,8 +301,8 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init: encoding/binary.init github.com/goplus/llgo/cl/_testmeta/interface_exported_var.init$guard github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: - _llgo_encoding/binary.ByteOrder _llgo_encoding/binary.littleEndian + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw encoding/binary.LittleEndian github.com/goplus/llgo/runtime/internal/runtime.AllocU github.com/goplus/llgo/runtime/internal/runtime.AllocZ @@ -316,7 +316,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: - _llgo_encoding/binary.ByteOrder Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus [MethodInfo] *_llgo_encoding/binary.littleEndian: @@ -345,7 +345,7 @@ _llgo_encoding/binary.littleEndian: 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 [InterfaceInfo] -_llgo_encoding/binary.ByteOrder: +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index 6a319f3b41..e13b51bb7c 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -1,23 +1,23 @@ [TypeChildren] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: +*_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [OrdinaryEdges] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T +*_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M: @@ -30,15 +30,15 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: - *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I$imethods -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I$imethods: - _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: + *_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: github.com/goplus/llgo/cl/_testmeta/interface_named.T.M github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref @@ -46,8 +46,8 @@ github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M: github.com/goplus/llgo/cl/_testmeta/interface_named.init: github.com/goplus/llgo/cl/_testmeta/interface_named.init$guard github.com/goplus/llgo/cl/_testmeta/interface_named.main: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 github.com/goplus/llgo/cl/_testmeta/interface_named.use github.com/goplus/llgo/runtime/internal/runtime.AllocU github.com/goplus/llgo/runtime/internal/runtime.NewItab @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_named.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: @@ -69,6 +69,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M [InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index a095899883..26ce33099c 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -1,23 +1,23 @@ [TypeChildren] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: +*github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [OrdinaryEdges] *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T +*github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m: @@ -30,12 +30,6 @@ __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac: *_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: - *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I - __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I$imethods -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I$imethods: - _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 @@ -43,11 +37,17 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m: github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: + *github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo$imethods: + _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.init: github.com/goplus/llgo/cl/_testmeta/interface_unexported.init$guard github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo github.com/goplus/llgo/cl/_testmeta/interface_unexported.use github.com/goplus/llgo/runtime/internal/runtime.AllocU github.com/goplus/llgo/runtime/internal/runtime.NewItab @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: @@ -69,6 +69,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m [InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index 882c4cba46..347b2bbe46 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -49,6 +49,8 @@ _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw *_llgo_int: _llgo_int *_llgo_int32: @@ -129,6 +131,8 @@ _llgo_func$w4tN9iibS_UimF5vLUWoKP0uAk2tJZF26VqETo_8LVg: _llgo_uint8 _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_string +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Reader: _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Writer: @@ -210,6 +214,9 @@ _llgo_io.Writer: *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw *_llgo_int: __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr _llgo_int @@ -456,6 +463,12 @@ _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: _llgo_string +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + *_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_int: *_llgo_int __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 @@ -493,7 +506,7 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.init: io.init github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: *_llgo_bytes.Buffer - _llgo_io.Reader + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw bytes.NewBuffer github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData github.com/goplus/llgo/runtime/internal/runtime.NewItab @@ -504,7 +517,7 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: - _llgo_io.Reader Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk [MethodInfo] *_llgo_bytes.Buffer: @@ -539,6 +552,8 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: [InterfaceInfo] _llgo_error: Error _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Reader: Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Writer: diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index 54e2cbf315..c48038e353 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -52,8 +52,8 @@ github.com/goplus/llgo/cl/_testmeta/reflect_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: - _llgo_reflect.Type[21] - _llgo_reflect.Type[21] + github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI[21] + github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI[21] [UseNamedMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: diff --git a/ssa/interface.go b/ssa/interface.go index a4f31fbc63..b3a9c53817 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -42,12 +42,11 @@ func (b Builder) newItab(tintf, typ Expr) Expr { return b.Call(b.Pkg.rtFunc("NewItab"), tintf, typ) } -func (b Builder) unsafeInterface(intfType types.Type, t Expr, data llvm.Value) llvm.Value { - rawIntf := intfType.Underlying().(*types.Interface) +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(intfType) + tintf := b.abiType(rawIntf) itab := b.newItab(tintf, t) return b.unsafeIface(itab.impl, data) } @@ -83,7 +82,7 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { tclosure := prog.Type(sig, InGo) i := iMethodOf(rawIntf, method.Name()) if mb := b.Pkg.MetaBuilder; mb != nil { - intfSym := mb.Sym(func() string { n, _ := prog.abi.TypeName(intf.raw.Type); return n }()) + intfSym := mb.Sym(func() string { n, _ := prog.abi.TypeName(rawIntf); return n }()) // Record which interface method is demanded. i is the method's index in rawIntf. // Interface method sets are emitted when the interface type descriptor is built. mb.AddEdge(mb.Sym(b.Func.Name()), intfSym, meta.EdgeUseIfaceMethod, uint32(i)) @@ -136,14 +135,14 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { if !directIfaceType(typ.raw.Type) { vptr := b.AllocU(typ) b.Store(vptr, x) - return Expr{b.unsafeInterface(tinter.raw.Type, tabi, vptr.impl), tinter} + return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter} } kind, _, lvl := abi.DataKindOf(typ.raw.Type, 0, prog.is32Bits) switch kind { case abi.Indirect: vptr := b.AllocU(typ) b.Store(vptr, x) - return Expr{b.unsafeInterface(tinter.raw.Type, tabi, vptr.impl), tinter} + return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter} } ximpl := x.impl if lvl > 0 { @@ -152,7 +151,7 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { var u llvm.Value switch kind { case abi.Pointer: - return Expr{b.unsafeInterface(tinter.raw.Type, tabi, ximpl), tinter} + return Expr{b.unsafeInterface(rawIntf, tabi, ximpl), tinter} case abi.Integer: tu := prog.Uintptr() u = llvm.CreateIntCast(b.impl, ximpl, tu.ll) @@ -167,10 +166,11 @@ func (b Builder) MakeInterface(tinter Type, x Expr) (ret Expr) { panic("todo") } data := llvm.CreateIntToPtr(b.impl, u, prog.tyVoidPtr()) - return Expr{b.unsafeInterface(tinter.raw.Type, tabi, data), tinter} + return Expr{b.unsafeInterface(rawIntf, tabi, data), tinter} } func (b Builder) MakeInterfaceFromPtr(tinter Type, ptr Expr) (ret Expr) { + rawIntf := tinter.raw.Type.Underlying().(*types.Interface) prog := b.Prog b.AssertNilDeref(ptr) @@ -185,7 +185,7 @@ func (b Builder) MakeInterfaceFromPtr(tinter Type, ptr Expr) (ret Expr) { dst := b.Convert(prog.VoidPtr(), vptr) src := b.Convert(prog.VoidPtr(), ptr) b.Call(b.Pkg.rtFunc("Typedmemmove"), tabi, dst, src) - return Expr{b.unsafeInterface(tinter.raw.Type, tabi, vptr.impl), tinter} + return Expr{b.unsafeInterface(rawIntf, tabi, vptr.impl), tinter} } func (b Builder) recordUseIface(typ Type) { @@ -310,9 +310,9 @@ func (b Builder) TypeAssert(x Expr, assertedTyp Type, commaOk bool) Expr { eq = b.BinOp(token.NEQ, tx, b.Prog.Zero(b.Prog.AbiTypePtr())) val = func() Expr { return x } } else { - if _, ok := assertedTyp.raw.Type.Underlying().(*types.Interface); ok { + if rawIntf, ok := assertedTyp.raw.Type.Underlying().(*types.Interface); ok { eq = b.InlineCall(b.Pkg.rtFunc("Implements"), tabi, tx) - val = func() Expr { return Expr{b.unsafeInterface(assertedTyp.raw.Type, tx, b.faceData(x.impl)), assertedTyp} } + val = func() Expr { return Expr{b.unsafeInterface(rawIntf, tx, b.faceData(x.impl)), assertedTyp} } } else if assertedTyp.kind == vkClosure { eq = b.InlineCall(b.Pkg.rtFunc("MatchesClosure"), tabi, tx) val = func() Expr { return b.valFromData(assertedTyp, b.faceData(x.impl)) } @@ -376,9 +376,10 @@ func typeAssertMissingMethod(assertedTyp Type) string { // // t1 = change interface interface{} <- I (t0) func (b Builder) ChangeInterface(typ Type, x Expr) (ret Expr) { + rawIntf := typ.raw.Type.Underlying().(*types.Interface) tabi := b.faceAbiType(x) data := b.faceData(x.impl) - return Expr{b.unsafeInterface(typ.raw.Type, tabi, data), typ} + return Expr{b.unsafeInterface(rawIntf, tabi, data), typ} } // ----------------------------------------------------------------------------- From eb81ecbfdab0023b912cdba1f1acb6b16fd437ae Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jun 2026 16:39:12 +0800 Subject: [PATCH 24/37] fix deadcode interface method demands --- ssa/abi/abi.go | 2 +- ssa/abi/abi_coverage_test.go | 16 ++++++++++++++++ ssa/interface.go | 12 ++++++++++-- ssa/ssa_test.go | 11 +++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/ssa/abi/abi.go b/ssa/abi/abi.go index 78e00d18f1..138b364cfa 100644 --- a/ssa/abi/abi.go +++ b/ssa/abi/abi.go @@ -349,7 +349,7 @@ func (b *Builder) interfaceHash(t *types.Interface) (ret []byte, pkg string) { for i := 0; i < n; i++ { m := t.Method(i) if !m.Exported() && pkg == "" { - pkg = m.Pkg().Path() + pkg = PathOf(m.Pkg()) } ft := b.FuncName(m.Type().(*types.Signature)) fmt.Fprintln(h, m.Name(), ft) diff --git a/ssa/abi/abi_coverage_test.go b/ssa/abi/abi_coverage_test.go index 462a61624d..8b28d91497 100644 --- a/ssa/abi/abi_coverage_test.go +++ b/ssa/abi/abi_coverage_test.go @@ -286,6 +286,22 @@ func TestStructInterfaceClosureAndPathCoverage(t *testing.T) { t.Fatalf("InterfaceName(private iface)=(%q,%v), want %q*,false", got, pub, pkg.Path()+".iface$") } + realReflectlite := types.NewPackage("internal/reflectlite", "reflectlite") + patchReflectlite := types.NewPackage(PatchPathPrefix+"internal/reflectlite", "reflectlite") + realIface := types.NewInterfaceType([]*types.Func{ + types.NewFunc(token.NoPos, realReflectlite, "common", sig), + }, nil) + realIface.Complete() + patchIface := types.NewInterfaceType([]*types.Func{ + types.NewFunc(token.NoPos, patchReflectlite, "common", sig), + }, nil) + patchIface.Complete() + realName, _ := b.InterfaceName(realIface) + patchName, _ := b.InterfaceName(patchIface) + if realName != patchName { + t.Fatalf("InterfaceName should canonicalize patch package path: real=%q patch=%q", realName, patchName) + } + if got := PathOf(nil); got != "" { t.Fatalf("PathOf(nil)=%q, want empty", got) } diff --git a/ssa/interface.go b/ssa/interface.go index b3a9c53817..33e562d141 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -66,7 +66,8 @@ func iMethodOf(rawIntf *types.Interface, name string) int { // Imethod returns closure of an interface method. func (b Builder) Imethod(intf Expr, method *types.Func) Expr { prog := b.Prog - rawIntf := intf.raw.Type.Underlying().(*types.Interface) + intfType := types.Unalias(intf.raw.Type) + rawIntf := intfType.Underlying().(*types.Interface) sig := method.Type().(*types.Signature) if sig.Recv() == nil && sig.Params().Len() > 0 { pt := types.Unalias(sig.Params().At(0).Type()) @@ -82,7 +83,14 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { tclosure := prog.Type(sig, InGo) i := iMethodOf(rawIntf, method.Name()) if mb := b.Pkg.MetaBuilder; mb != nil { - intfSym := mb.Sym(func() string { n, _ := prog.abi.TypeName(rawIntf); return n }()) + // Keep the demand owner aligned with the interface type descriptor that + // emits InterfaceInfo. Named interfaces must not be collapsed to their + // structural underlying interface here. + intfSymType := intfType + if _, ok := intfSymType.(*types.TypeParam); ok { + intfSymType = rawIntf + } + intfSym := mb.Sym(func() string { n, _ := prog.abi.TypeName(intfSymType); return n }()) // Record which interface method is demanded. i is the method's index in rawIntf. // Interface method sets are emitted when the interface type descriptor is built. mb.AddEdge(mb.Sym(b.Func.Name()), intfSym, meta.EdgeUseIfaceMethod, uint32(i)) diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 74d4242286..85778d2af6 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -34,6 +34,7 @@ import ( "unsafe" "github.com/goplus/gogen/packages" + "github.com/goplus/llgo/internal/meta" rtabi "github.com/goplus/llgo/runtime/abi" "github.com/xgo-dev/llvm" ) @@ -1190,6 +1191,7 @@ func TestIfaceMethodClosureCallIR(t *testing.T) { recvMeth := types.NewFunc(0, pkgTypes, "Printf", recvSig) pkg := prog.NewPackage("bar", "foo/bar") + pkg.MetaBuilder = meta.NewBuilder() callerSig := types.NewSignatureType(nil, nil, nil, types.NewTuple(types.NewVar(0, pkgTypes, "i", namedIface)), types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])), false) @@ -1199,6 +1201,15 @@ func TestIfaceMethodClosureCallIR(t *testing.T) { ret := b.Call(closure, prog.Val(100), prog.Val(200)) b.Return(ret) + pm, err := pkg.MetaBuilder.Build() + if err != nil { + t.Fatal(err) + } + defer pm.Close() + if got := pm.String(); !strings.Contains(got, "_llgo_foo/bar.IFmt[0]") { + t.Fatalf("UseIfaceMethod should target named interface symbol, got:\n%s", got) + } + assertPkg(t, pkg, `; ModuleID = 'foo/bar' source_filename = "foo/bar" From d2b514d7cbcd981b0d4edece041a2fe824892621 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jun 2026 19:02:08 +0800 Subject: [PATCH 25/37] fix deadcode interface info for method demands --- ssa/abitype.go | 23 +++++++++++++++-------- ssa/interface.go | 7 +++++-- ssa/ssa_test.go | 8 ++++++-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/ssa/abitype.go b/ssa/abitype.go index 71c8f8a88d..aa37072dc4 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -205,14 +205,7 @@ func (b Builder) abiInterfaceImethods(t *types.Interface, typeName string) llvm. if n == 0 { return prog.Nil(prog.rtType("Slice")).impl } - if mb := b.Pkg.MetaBuilder; mb != nil { - intfSym := mb.Sym(typeName) - for i := 0; i < n; i++ { - f := t.Method(i) - ftypName, _ := prog.abi.TypeName(funcType(prog, f.Type())) - mb.AddIfaceMethod(intfSym, mthName(f), mb.Sym(ftypName)) - } - } + b.recordInterfaceInfo(t, typeName) imethodsName := typeName + "$imethods" g := b.Pkg.VarOf(imethodsName) @@ -246,6 +239,20 @@ func (b Builder) abiInterfaceImethods(t *types.Interface, typeName string) llvm. }) } +func (b Builder) recordInterfaceInfo(t *types.Interface, typeName string) { + mb := b.Pkg.MetaBuilder + if mb == nil { + return + } + prog := b.Prog + intfSym := mb.Sym(typeName) + for i := 0; i < t.NumMethods(); i++ { + f := t.Method(i) + ftypName, _ := prog.abi.TypeName(funcType(prog, f.Type())) + mb.AddIfaceMethod(intfSym, mthName(f), mb.Sym(ftypName)) + } +} + func (b Builder) abiTuples(t *types.Tuple, name string) llvm.Value { prog := b.Prog n := t.Len() diff --git a/ssa/interface.go b/ssa/interface.go index 33e562d141..abd1b179a9 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -90,9 +90,12 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { if _, ok := intfSymType.(*types.TypeParam); ok { intfSymType = rawIntf } - intfSym := mb.Sym(func() string { n, _ := prog.abi.TypeName(intfSymType); return n }()) + intfSymName := func() string { n, _ := prog.abi.TypeName(intfSymType); return n }() + b.recordInterfaceInfo(rawIntf, intfSymName) + intfSym := mb.Sym(intfSymName) // Record which interface method is demanded. i is the method's index in rawIntf. - // Interface method sets are emitted when the interface type descriptor is built. + // Ensure the matching InterfaceInfo exists even if this interface type descriptor + // is not otherwise materialized. mb.AddEdge(mb.Sym(b.Func.Name()), intfSym, meta.EdgeUseIfaceMethod, uint32(i)) } data := b.InlineCall(b.Pkg.rtFunc("IfacePtrData"), intf) diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 85778d2af6..b14d15aa88 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -1206,8 +1206,12 @@ func TestIfaceMethodClosureCallIR(t *testing.T) { t.Fatal(err) } defer pm.Close() - if got := pm.String(); !strings.Contains(got, "_llgo_foo/bar.IFmt[0]") { - t.Fatalf("UseIfaceMethod should target named interface symbol, got:\n%s", got) + gotMeta := pm.String() + if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt Printf ") { + t.Fatalf("UseIfaceMethod should target named interface symbol, got:\n%s", gotMeta) + } + if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt:\n Printf ") { + t.Fatalf("InterfaceInfo should be recorded for named interface demand, got:\n%s", gotMeta) } assertPkg(t, pkg, `; ModuleID = 'foo/bar' From 019f13aa758c8beadba19f34f9824129735e9797 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jun 2026 19:35:16 +0800 Subject: [PATCH 26/37] update deadcode meta golden files --- .../interface_exported_var/meta-expect.txt | 10 ++++- cl/_testmeta/interface_named/meta-expect.txt | 4 +- .../interface_unexported/meta-expect.txt | 4 +- .../methodinfo_imported/meta-expect.txt | 2 +- cl/_testmeta/reflect_named/meta-expect.txt | 44 ++++++++++++++++++- 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt index 4be1e14c08..37d7ea02b3 100644 --- a/cl/_testmeta/interface_exported_var/meta-expect.txt +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -316,7 +316,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: - _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_encoding/binary.ByteOrder Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus [MethodInfo] *_llgo_encoding/binary.littleEndian: @@ -345,6 +345,14 @@ _llgo_encoding/binary.littleEndian: 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 [InterfaceInfo] +_llgo_encoding/binary.ByteOrder: + PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU + PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg + PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 + String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc + Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index e13b51bb7c..6ee47f82d8 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_named.use: - _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: @@ -69,6 +69,8 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M [InterfaceInfo] +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: + M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index 26ce33099c..67597ef3e0 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: - github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: @@ -69,6 +69,8 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m [InterfaceInfo] +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: + github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index 347b2bbe46..649cebb076 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -517,7 +517,7 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: - _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + _llgo_io.Reader Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk [MethodInfo] *_llgo_bytes.Buffer: diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index c48038e353..187a7c38b4 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -52,8 +52,8 @@ github.com/goplus/llgo/cl/_testmeta/reflect_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: - github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI[21] - github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI[21] + _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E [UseNamedMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: @@ -68,3 +68,43 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m +[InterfaceInfo] +_llgo_reflect.Type: + Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + AssignableTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Bits _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + CanSeq _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + CanSeq2 _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ChanDir _llgo_func$JO3khPIbANSMBmoN6P7ybYAeUBd3Gv6toVUqNeE7qbE + Comparable _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ConvertibleTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Elem _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Field _llgo_func$Q3NYrysaKgu1MtMuLQwb-k5QcKGHihnt-tV_NlNJQFA + FieldAlign _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + FieldByIndex _llgo_func$LPPtiM49dEPl48CC3WRhXm3YPnfUJEZE_k8Tx3rMuSk + FieldByName _llgo_func$dEvABJ5r0MMUlf4smWpIDG5dO8AuGklGdNJ1xneL3UM + FieldByNameFunc _llgo_func$xySrXVFC_2LK2oP71R2UryKi6UmdEJUo9k6aQuz4TvI + Implements _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + In _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + IsVariadic _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + Key _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Kind _llgo_func$w8Mj2LK8G5p7MIiGWR6MYjyXy3L8SVVzYlT1bb6KNXk + Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Method _llgo_func$FmJJGomlX5kINJGxQdQDCAkD89ySoMslAYFrziWInVc + MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + Name _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + NumField _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumIn _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumMethod _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumOut _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Out _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + OverflowComplex _llgo_func$cGkbH-2LQOLoq64Rqj3WeO56U8al7FfVkf5K1FFbPpE + OverflowFloat _llgo_func$uk7PgUVap9GZdvS8R_mZCDbAbqnAbcNryqybtDogUNI + OverflowInt _llgo_func$odFOIClZoEVGbTP_BEfZxVM5ex3r8Fj1afUEeP_awp8 + OverflowUint _llgo_func$7I97sofX8UqJA96mVIy89KPUfSM_efkrR-mJQ9qaHfk + PkgPath _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + Size _llgo_func$1kITCsyu7hFLMxHLR7kDlvu4SOra_HtrtdFUQH9P13s + String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + reflect.common _llgo_func$w6XuV-1SmW103DbauPseXBpW50HpxXAEsUsGFibl0Uw + reflect.uncommon _llgo_func$iG49bujiXjI2lVflYdE0hPXlCAABL-XKRANSNJEKOio + From 092af96f14b4d26564151790c2324698a9353767 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 1 Jul 2026 12:26:53 +0800 Subject: [PATCH 27/37] Emit interface metadata from defining packages --- .../interface_exported_var/meta-expect.txt | 10 +- cl/_testmeta/interface_imported/api/api.go | 15 ++ .../interface_imported/api/meta-expect.txt | 16 ++ cl/_testmeta/interface_imported/in.go | 12 ++ .../interface_imported/meta-expect.txt | 167 ++++++++++++++++++ cl/_testmeta/reflect_named/meta-expect.txt | 44 +---- cl/cltest/cltest.go | 81 ++++++++- cl/compile.go | 1 + ssa/abitype.go | 38 +++- ssa/interface.go | 11 +- ssa/ssa_test.go | 6 +- 11 files changed, 335 insertions(+), 66 deletions(-) create mode 100644 cl/_testmeta/interface_imported/api/api.go create mode 100644 cl/_testmeta/interface_imported/api/meta-expect.txt create mode 100644 cl/_testmeta/interface_imported/in.go create mode 100644 cl/_testmeta/interface_imported/meta-expect.txt diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt index 37d7ea02b3..cb2a43e790 100644 --- a/cl/_testmeta/interface_exported_var/meta-expect.txt +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -316,7 +316,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: - _llgo_encoding/binary.ByteOrder Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_encoding/binary.ByteOrder[4] [MethodInfo] *_llgo_encoding/binary.littleEndian: @@ -345,14 +345,6 @@ _llgo_encoding/binary.littleEndian: 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 [InterfaceInfo] -_llgo_encoding/binary.ByteOrder: - PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU - PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg - PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 - String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus - Uint32 _llgo_func$ICfGJV9Kp4o-2SMi1iuyC9xBBX1c5utHD63uVbyrfEc - Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg diff --git a/cl/_testmeta/interface_imported/api/api.go b/cl/_testmeta/interface_imported/api/api.go new file mode 100644 index 0000000000..e6ce2af1bc --- /dev/null +++ b/cl/_testmeta/interface_imported/api/api.go @@ -0,0 +1,15 @@ +package api + +type Reader interface { + Read([]byte) (int, error) +} + +type Source struct{} + +func (Source) Read([]byte) (int, error) { + return 7, nil +} + +func (Source) Close() error { + return nil +} diff --git a/cl/_testmeta/interface_imported/api/meta-expect.txt b/cl/_testmeta/interface_imported/api/meta-expect.txt new file mode 100644 index 0000000000..301c517530 --- /dev/null +++ b/cl/_testmeta/interface_imported/api/meta-expect.txt @@ -0,0 +1,16 @@ +[OrdinaryEdges] +github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Close + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read + github.com/goplus/llgo/runtime/internal/runtime.AssertNilDeref + github.com/goplus/llgo/runtime/internal/runtime.PanicWrapNilPointer +github.com/goplus/llgo/cl/_testmeta/interface_imported/api.init: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.init$guard + +[InterfaceInfo] +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Reader: + Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + diff --git a/cl/_testmeta/interface_imported/in.go b/cl/_testmeta/interface_imported/in.go new file mode 100644 index 0000000000..d54efd2f07 --- /dev/null +++ b/cl/_testmeta/interface_imported/in.go @@ -0,0 +1,12 @@ +package main + +import "github.com/goplus/llgo/cl/_testmeta/interface_imported/api" + +func use(r api.Reader) int { + n, _ := r.Read(nil) + return n +} + +func main() { + _ = use(api.Source{}) +} diff --git a/cl/_testmeta/interface_imported/meta-expect.txt b/cl/_testmeta/interface_imported/meta-expect.txt new file mode 100644 index 0000000000..d397a163d5 --- /dev/null +++ b/cl/_testmeta/interface_imported/meta-expect.txt @@ -0,0 +1,167 @@ +[TypeChildren] +*[]_llgo_uint8: + []_llgo_uint8 +*_llgo_error: + _llgo_error +*_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w +*_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw +*_llgo_int: + _llgo_int +*_llgo_string: + _llgo_string +*_llgo_uint8: + _llgo_uint8 +[]_llgo_uint8: + _llgo_uint8 +_llgo_error: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + _llgo_error +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + []_llgo_uint8 + _llgo_error + _llgo_int +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_string +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + +[OrdinaryEdges] +*[]_llgo_uint8: + []_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr +*_llgo_error: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_error +*_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w +*_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source +*_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +*_llgo_uint8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_uint8 +[]_llgo_uint8: + *[]_llgo_uint8 + _llgo_uint8 +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Close: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Close +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read: + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0: + github.com/goplus/llgo/runtime/internal/runtime.memequal0 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8: + github.com/goplus/llgo/runtime/internal/runtime.memequal8 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_error: + *_llgo_error + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_error$imethods +_llgo_error$imethods: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w: + *_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w + _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w$out +_llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w$out: + _llgo_error +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk: + *_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$in + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$out +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$in: + []_llgo_uint8 +_llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk$out: + _llgo_error + _llgo_int +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal0 +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + *_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw$imethods: + _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk +_llgo_int: + *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +_llgo_string: + *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_uint8: + *_llgo_uint8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal8 +github.com/goplus/llgo/cl/_testmeta/interface_imported.init: + github.com/goplus/llgo/cl/_testmeta/interface_imported.init$guard + github.com/goplus/llgo/cl/_testmeta/interface_imported/api.init +github.com/goplus/llgo/cl/_testmeta/interface_imported.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw + github.com/goplus/llgo/cl/_testmeta/interface_imported.use + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.NewItab +github.com/goplus/llgo/cl/_testmeta/interface_imported.use: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_imported.main: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_imported.use: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Reader[0] + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + 0 Close _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close + 1 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: + 0 Close _llgo_func$8rsrSd_r3UHd_2DiYTyaOKR7BYkei4zw5ysG35KF38w github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Close __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Close + 1 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read + +[InterfaceInfo] +_llgo_error: + Error _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: + Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index 187a7c38b4..54e2cbf315 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -52,8 +52,8 @@ github.com/goplus/llgo/cl/_testmeta/reflect_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: - _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E - _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + _llgo_reflect.Type[21] + _llgo_reflect.Type[21] [UseNamedMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: @@ -68,43 +68,3 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m -[InterfaceInfo] -_llgo_reflect.Type: - Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - AssignableTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE - Bits _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - CanSeq _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - CanSeq2 _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - ChanDir _llgo_func$JO3khPIbANSMBmoN6P7ybYAeUBd3Gv6toVUqNeE7qbE - Comparable _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - ConvertibleTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE - Elem _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI - Field _llgo_func$Q3NYrysaKgu1MtMuLQwb-k5QcKGHihnt-tV_NlNJQFA - FieldAlign _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - FieldByIndex _llgo_func$LPPtiM49dEPl48CC3WRhXm3YPnfUJEZE_k8Tx3rMuSk - FieldByName _llgo_func$dEvABJ5r0MMUlf4smWpIDG5dO8AuGklGdNJ1xneL3UM - FieldByNameFunc _llgo_func$xySrXVFC_2LK2oP71R2UryKi6UmdEJUo9k6aQuz4TvI - Implements _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE - In _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY - IsVariadic _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk - Key _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI - Kind _llgo_func$w8Mj2LK8G5p7MIiGWR6MYjyXy3L8SVVzYlT1bb6KNXk - Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - Method _llgo_func$FmJJGomlX5kINJGxQdQDCAkD89ySoMslAYFrziWInVc - MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E - Name _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - NumField _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - NumIn _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - NumMethod _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - NumOut _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA - Out _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY - OverflowComplex _llgo_func$cGkbH-2LQOLoq64Rqj3WeO56U8al7FfVkf5K1FFbPpE - OverflowFloat _llgo_func$uk7PgUVap9GZdvS8R_mZCDbAbqnAbcNryqybtDogUNI - OverflowInt _llgo_func$odFOIClZoEVGbTP_BEfZxVM5ex3r8Fj1afUEeP_awp8 - OverflowUint _llgo_func$7I97sofX8UqJA96mVIy89KPUfSM_efkrR-mJQ9qaHfk - PkgPath _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - Size _llgo_func$1kITCsyu7hFLMxHLR7kDlvu4SOra_HtrtdFUQH9P13s - String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to - reflect.common _llgo_func$w6XuV-1SmW103DbauPseXBpW50HpxXAEsUsGFibl0Uw - reflect.uncommon _llgo_func$iG49bujiXjI2lVflYdE0hPXlCAABL-XKRANSNJEKOio - diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 9b0fa54428..5034f5e2f5 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -286,13 +286,17 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption testFrom(t, pkgDir, sel) } if opts.checkMeta { - conf, _, capturedMeta := withModuleCapture(opts.conf, pkgDir) + metaDirs, err := findMetaCheckDirs(pkgDir) + if err != nil { + t.Fatal("Find meta check dirs failed:", err) + } + conf, capturedMetas := withMetaCaptures(opts.conf, metaDirs) output, err := runWithConf(relPkg, pkgDir, conf) if err != nil { t.Logf("raw output:\n%s", string(output)) t.Fatalf("run failed: %v\noutput: %s", err, string(output)) } - assertExpectedMeta(t, pkgDir, relPkg, capturedMeta) + assertExpectedMetas(t, pkgDir, relPkg, capturedMetas) } return } @@ -354,6 +358,29 @@ func assertExpectedMeta(t *testing.T, pkgDir, relPkg string, capturedMeta *strin } } +func assertExpectedMetas(t *testing.T, rootDir, relRoot string, capturedMetas map[string]*string) { + t.Helper() + if len(capturedMetas) == 0 { + t.Fatal("missing meta-expect.txt") + } + dirs := make([]string, 0, len(capturedMetas)) + for dir := range capturedMetas { + dirs = append(dirs, dir) + } + slices.Sort(dirs) + for _, dir := range dirs { + relPkg := relRoot + if dir != rootDir { + rel, err := filepath.Rel(rootDir, dir) + if err != nil { + t.Fatal("Rel failed:", err) + } + relPkg = strings.TrimSuffix(relRoot, "/") + "/" + filepath.ToSlash(rel) + } + assertExpectedMeta(t, dir, relPkg, capturedMetas[dir]) + } +} + func RunAndCapture(relPkg, pkgDir string) ([]byte, error) { conf := build.NewDefaultConf(build.ModeRun) return RunAndCaptureWithConf(relPkg, pkgDir, conf) @@ -396,6 +423,56 @@ func withModuleCapture(conf *build.Config, pkgDir string) (*build.Config, *strin return &localConf, &module, &meta } +func findMetaCheckDirs(pkgDir string) ([]string, error) { + var dirs []string + err := filepath.WalkDir(pkgDir, func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() { + return nil + } + if _, err := os.Stat(filepath.Join(path, "meta-expect.txt")); err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil + } + return err + } + dirs = append(dirs, filepath.Clean(path)) + return nil + }) + if err != nil { + return nil, err + } + slices.Sort(dirs) + return dirs, nil +} + +func withMetaCaptures(conf *build.Config, pkgDirs []string) (*build.Config, map[string]*string) { + if conf == nil { + conf = build.NewDefaultConf(build.ModeRun) + } + localConf := *conf + localConf.ForceRebuild = true + metas := make(map[string]*string, len(pkgDirs)) + for _, pkgDir := range pkgDirs { + metas[filepath.Clean(pkgDir)] = new(string) + } + prevHook := localConf.ModuleHook + localConf.ModuleHook = func(pkg build.Package) { + if prevHook != nil { + prevHook(pkg) + } + for _, file := range pkg.Package.GoFiles { + if meta := metas[filepath.Clean(filepath.Dir(file))]; meta != nil { + *meta = pkg.Meta.String() + return + } + } + } + return &localConf, metas +} + func testBuildAndCheckSymbolsFrom(t *testing.T, pkgDir, relPkg, sel, symbolSpec string, opts runOptions) { t.Helper() if sel != "" && !strings.Contains(pkgDir, sel) { diff --git a/cl/compile.go b/cl/compile.go index fe17d6a608..956c09f76f 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -1759,6 +1759,7 @@ func newPackageEx(prog llssa.Program, patches Patches, rewrites map[string]strin if !ctx.skipall { processPkg(ctx, ret, pkg) } + ret.RecordDefinedInterfaceInfo(pkgTypes) for len(ctx.inits) > 0 { inits := ctx.inits ctx.inits = nil diff --git a/ssa/abitype.go b/ssa/abitype.go index aa37072dc4..0f9d24ceca 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -205,7 +205,7 @@ func (b Builder) abiInterfaceImethods(t *types.Interface, typeName string) llvm. if n == 0 { return prog.Nil(prog.rtType("Slice")).impl } - b.recordInterfaceInfo(t, typeName) + b.Pkg.recordInterfaceInfo(t, typeName) imethodsName := typeName + "$imethods" g := b.Pkg.VarOf(imethodsName) @@ -239,12 +239,42 @@ func (b Builder) abiInterfaceImethods(t *types.Interface, typeName string) llvm. }) } -func (b Builder) recordInterfaceInfo(t *types.Interface, typeName string) { - mb := b.Pkg.MetaBuilder +func (p Package) RecordDefinedInterfaceInfo(pkgTypes *types.Package) { + mb := p.MetaBuilder + if mb == nil || pkgTypes == nil { + return + } + scope := pkgTypes.Scope() + for _, name := range scope.Names() { + obj, ok := scope.Lookup(name).(*types.TypeName) + if !ok { + continue + } + if _, ok := obj.Type().(*types.Alias); ok { + continue + } + named, ok := types.Unalias(obj.Type()).(*types.Named) + if !ok { + continue + } + if tparams := named.TypeParams(); tparams != nil && tparams.Len() > 0 { + continue + } + iface, ok := named.Underlying().(*types.Interface) + if !ok || iface.NumMethods() == 0 { + continue + } + typeName, _ := p.Prog.abi.TypeName(named) + p.recordInterfaceInfo(iface, typeName) + } +} + +func (p Package) recordInterfaceInfo(t *types.Interface, typeName string) { + mb := p.MetaBuilder if mb == nil { return } - prog := b.Prog + prog := p.Prog intfSym := mb.Sym(typeName) for i := 0; i < t.NumMethods(); i++ { f := t.Method(i) diff --git a/ssa/interface.go b/ssa/interface.go index abd1b179a9..5950ae70cc 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -83,19 +83,18 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { tclosure := prog.Type(sig, InGo) i := iMethodOf(rawIntf, method.Name()) if mb := b.Pkg.MetaBuilder; mb != nil { - // Keep the demand owner aligned with the interface type descriptor that - // emits InterfaceInfo. Named interfaces must not be collapsed to their - // structural underlying interface here. intfSymType := intfType if _, ok := intfSymType.(*types.TypeParam); ok { intfSymType = rawIntf } + if named, ok := types.Unalias(intfSymType).(*types.Named); ok { + if targs := named.TypeArgs(); targs != nil && targs.Len() > 0 { + intfSymType = rawIntf + } + } intfSymName := func() string { n, _ := prog.abi.TypeName(intfSymType); return n }() - b.recordInterfaceInfo(rawIntf, intfSymName) intfSym := mb.Sym(intfSymName) // Record which interface method is demanded. i is the method's index in rawIntf. - // Ensure the matching InterfaceInfo exists even if this interface type descriptor - // is not otherwise materialized. mb.AddEdge(mb.Sym(b.Func.Name()), intfSym, meta.EdgeUseIfaceMethod, uint32(i)) } data := b.InlineCall(b.Pkg.rtFunc("IfacePtrData"), intf) diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index b14d15aa88..7bc63ab9ca 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -1207,11 +1207,11 @@ func TestIfaceMethodClosureCallIR(t *testing.T) { } defer pm.Close() gotMeta := pm.String() - if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt Printf ") { + if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt[0]") { t.Fatalf("UseIfaceMethod should target named interface symbol, got:\n%s", gotMeta) } - if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt:\n Printf ") { - t.Fatalf("InterfaceInfo should be recorded for named interface demand, got:\n%s", gotMeta) + if strings.Contains(gotMeta, "_llgo_foo/bar.IFmt:\n Printf ") { + t.Fatalf("UseIfaceMethod callsite should not record InterfaceInfo, got:\n%s", gotMeta) } assertPkg(t, pkg, `; ModuleID = 'foo/bar' From 77f72aea687bdf755317efaf7adde1d20bccb198 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 1 Jul 2026 14:51:36 +0800 Subject: [PATCH 28/37] Add generic interface metadata coverage --- cl/_testmeta/interface_generic/in.go | 21 +++++ .../interface_generic/meta-expect.txt | 84 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 cl/_testmeta/interface_generic/in.go create mode 100644 cl/_testmeta/interface_generic/meta-expect.txt diff --git a/cl/_testmeta/interface_generic/in.go b/cl/_testmeta/interface_generic/in.go new file mode 100644 index 0000000000..ebf78edde5 --- /dev/null +++ b/cl/_testmeta/interface_generic/in.go @@ -0,0 +1,21 @@ +package main + +type Box[T any] struct { + value T +} + +func (b *Box[T]) Value() T { + return b.value +} + +type I[T any] interface { + Value() T +} + +func useInt(v I[int]) int { + return v.Value() +} + +func main() { + _ = useInt(&Box[int]{value: 42}) +} diff --git a/cl/_testmeta/interface_generic/meta-expect.txt b/cl/_testmeta/interface_generic/meta-expect.txt new file mode 100644 index 0000000000..d1ddbb64fa --- /dev/null +++ b/cl/_testmeta/interface_generic/meta-expect.txt @@ -0,0 +1,84 @@ +[TypeChildren] +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] +*_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 +*_llgo_int: + _llgo_int +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_int +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + _llgo_int +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + +[OrdinaryEdges] +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] +*_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic.(*Box[int]).Value: + github.com/goplus/llgo/cl/_testmeta/interface_generic.(*Box[int]).Value +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + *_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out: + _llgo_int +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] + github.com/goplus/llgo/cl/_testmeta/interface_generic.struct$lOhriNu2BrWBR2Mh8k-KggMYlAw0Wx-2ftE2_gNyubg$fields + github.com/goplus/llgo/runtime/internal/runtime.structequal +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + *_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +_llgo_int: + *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +github.com/goplus/llgo/cl/_testmeta/interface_generic.init: + github.com/goplus/llgo/cl/_testmeta/interface_generic.init$guard +github.com/goplus/llgo/cl/_testmeta/interface_generic.main: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + github.com/goplus/llgo/cl/_testmeta/interface_generic.useInt + github.com/goplus/llgo/runtime/internal/runtime.AllocZ + github.com/goplus/llgo/runtime/internal/runtime.NewItab +github.com/goplus/llgo/cl/_testmeta/interface_generic.struct$lOhriNu2BrWBR2Mh8k-KggMYlAw0Wx-2ftE2_gNyubg$fields: + _llgo_int +github.com/goplus/llgo/cl/_testmeta/interface_generic.useInt: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_generic.main: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int] + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_generic.useInt: + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic.Box[int]: + 0 Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA github.com/goplus/llgo/cl/_testmeta/interface_generic.(*Box[int]).Value __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic.(*Box[int]).Value + +[InterfaceInfo] +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + From 6bd134a78fedf42f0ea01fed88fb0fe59bb3a151 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 1 Jul 2026 18:34:42 +0800 Subject: [PATCH 29/37] test: add deadcode drop regression suite --- cl/_testdrop/direct_func/expect.txt | 1 + cl/_testdrop/direct_func/in.go | 22 ++++++ cl/_testdrop/direct_method/expect.txt | 1 + cl/_testdrop/direct_method/in.go | 32 +++++++++ .../exported_method_crosspkg/api/api.go | 9 +++ .../exported_method_crosspkg/expect.txt | 1 + cl/_testdrop/exported_method_crosspkg/in.go | 29 ++++++++ .../generic_interface_crosspkg/api/api.go | 9 +++ .../generic_interface_crosspkg/expect.txt | 1 + cl/_testdrop/generic_interface_crosspkg/in.go | 30 ++++++++ .../generic_interface_crosspkg/model/model.go | 27 +++++++ .../api/api.go | 9 +++ .../expect.txt | 1 + .../generic_interface_func_crosspkg/in.go | 38 ++++++++++ .../model/model.go | 31 ++++++++ cl/_testdrop/iface_flow_crosspkg/api/api.go | 9 +++ cl/_testdrop/iface_flow_crosspkg/expect.txt | 1 + .../iface_flow_crosspkg/factory/factory.go | 21 ++++++ cl/_testdrop/iface_flow_crosspkg/in.go | 18 +++++ .../interface_demand_fixedpoint/api/api.go | 28 ++++++++ .../interface_demand_fixedpoint/expect.txt | 1 + .../interface_demand_fixedpoint/flow/flow.go | 45 ++++++++++++ .../interface_demand_fixedpoint/in.go | 29 ++++++++ .../model/model.go | 21 ++++++ cl/_testdrop/interface_match/expect.txt | 1 + cl/_testdrop/interface_match/in.go | 72 +++++++++++++++++++ cl/_testdrop/interface_slot/expect.txt | 1 + cl/_testdrop/interface_slot/in.go | 35 +++++++++ .../promoted_method_wrapper/expect.txt | 1 + cl/_testdrop/promoted_method_wrapper/in.go | 39 ++++++++++ .../reflect_dynamic_iface_crosspkg/api/api.go | 12 ++++ .../reflect_dynamic_iface_crosspkg/expect.txt | 1 + .../reflect_dynamic_iface_crosspkg/in.go | 33 +++++++++ .../model/model.go | 31 ++++++++ cl/_testdrop/reflect_named_method/expect.txt | 1 + cl/_testdrop/reflect_named_method/in.go | 28 ++++++++ cl/_testdrop/source64_crosspkg/api/api.go | 28 ++++++++ cl/_testdrop/source64_crosspkg/expect.txt | 1 + cl/_testdrop/source64_crosspkg/in.go | 31 ++++++++ cl/_testdrop/source64_crosspkg/model/model.go | 48 +++++++++++++ .../unexported_method_identity/api/api.go | 22 ++++++ .../unexported_method_identity/expect.txt | 1 + cl/_testdrop/unexported_method_identity/in.go | 27 +++++++ cl/cltest/cltest.go | 35 ++++++++- cl/compile_test.go | 25 +++++++ 45 files changed, 886 insertions(+), 1 deletion(-) create mode 100644 cl/_testdrop/direct_func/expect.txt create mode 100644 cl/_testdrop/direct_func/in.go create mode 100644 cl/_testdrop/direct_method/expect.txt create mode 100644 cl/_testdrop/direct_method/in.go create mode 100644 cl/_testdrop/exported_method_crosspkg/api/api.go create mode 100644 cl/_testdrop/exported_method_crosspkg/expect.txt create mode 100644 cl/_testdrop/exported_method_crosspkg/in.go create mode 100644 cl/_testdrop/generic_interface_crosspkg/api/api.go create mode 100644 cl/_testdrop/generic_interface_crosspkg/expect.txt create mode 100644 cl/_testdrop/generic_interface_crosspkg/in.go create mode 100644 cl/_testdrop/generic_interface_crosspkg/model/model.go create mode 100644 cl/_testdrop/generic_interface_func_crosspkg/api/api.go create mode 100644 cl/_testdrop/generic_interface_func_crosspkg/expect.txt create mode 100644 cl/_testdrop/generic_interface_func_crosspkg/in.go create mode 100644 cl/_testdrop/generic_interface_func_crosspkg/model/model.go create mode 100644 cl/_testdrop/iface_flow_crosspkg/api/api.go create mode 100644 cl/_testdrop/iface_flow_crosspkg/expect.txt create mode 100644 cl/_testdrop/iface_flow_crosspkg/factory/factory.go create mode 100644 cl/_testdrop/iface_flow_crosspkg/in.go create mode 100644 cl/_testdrop/interface_demand_fixedpoint/api/api.go create mode 100644 cl/_testdrop/interface_demand_fixedpoint/expect.txt create mode 100644 cl/_testdrop/interface_demand_fixedpoint/flow/flow.go create mode 100644 cl/_testdrop/interface_demand_fixedpoint/in.go create mode 100644 cl/_testdrop/interface_demand_fixedpoint/model/model.go create mode 100644 cl/_testdrop/interface_match/expect.txt create mode 100644 cl/_testdrop/interface_match/in.go create mode 100644 cl/_testdrop/interface_slot/expect.txt create mode 100644 cl/_testdrop/interface_slot/in.go create mode 100644 cl/_testdrop/promoted_method_wrapper/expect.txt create mode 100644 cl/_testdrop/promoted_method_wrapper/in.go create mode 100644 cl/_testdrop/reflect_dynamic_iface_crosspkg/api/api.go create mode 100644 cl/_testdrop/reflect_dynamic_iface_crosspkg/expect.txt create mode 100644 cl/_testdrop/reflect_dynamic_iface_crosspkg/in.go create mode 100644 cl/_testdrop/reflect_dynamic_iface_crosspkg/model/model.go create mode 100644 cl/_testdrop/reflect_named_method/expect.txt create mode 100644 cl/_testdrop/reflect_named_method/in.go create mode 100644 cl/_testdrop/source64_crosspkg/api/api.go create mode 100644 cl/_testdrop/source64_crosspkg/expect.txt create mode 100644 cl/_testdrop/source64_crosspkg/in.go create mode 100644 cl/_testdrop/source64_crosspkg/model/model.go create mode 100644 cl/_testdrop/unexported_method_identity/api/api.go create mode 100644 cl/_testdrop/unexported_method_identity/expect.txt create mode 100644 cl/_testdrop/unexported_method_identity/in.go diff --git a/cl/_testdrop/direct_func/expect.txt b/cl/_testdrop/direct_func/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/direct_func/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/direct_func/in.go b/cl/_testdrop/direct_func/in.go new file mode 100644 index 0000000000..cf8410a738 --- /dev/null +++ b/cl/_testdrop/direct_func/in.go @@ -0,0 +1,22 @@ +// LITTEST +package main + +// SYMBOL-NOT: testdrop/direct_func{{.*}}Drop +// SYMBOL-DAG: testdrop/direct_func{{.*}}Keep +// SYMBOL-NOT: testdrop/direct_func{{.*}}Drop + +var keepFunc = Keep + +//go:noinline +func Keep() int { + return 42 +} + +//go:noinline +func Drop() int { + panic("Drop should be unreachable") +} + +func main() { + println(keepFunc()) +} diff --git a/cl/_testdrop/direct_method/expect.txt b/cl/_testdrop/direct_method/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/direct_method/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/direct_method/in.go b/cl/_testdrop/direct_method/in.go new file mode 100644 index 0000000000..a7ddb0cba5 --- /dev/null +++ b/cl/_testdrop/direct_method/in.go @@ -0,0 +1,32 @@ +// LITTEST +package main + +// SYMBOL-NOT: testdrop/direct_method{{.*}}T{{.*}}Drop +// SYMBOL-DAG: testdrop/direct_method{{.*}}T{{.*}}Keep +// SYMBOL-NOT: testdrop/direct_method{{.*}}T{{.*}}Drop + +var sink any +var keepMethod = T.Keep + +// This case keeps T's runtime type metadata reachable through an empty +// interface conversion. T.Drop must still be removed because no interface or +// direct call path requires that method slot. +type T struct { + n int +} + +//go:noinline +func (t T) Keep() int { + return t.n + 1 +} + +//go:noinline +func (t T) Drop() int { + panic("Drop should be unreachable") +} + +func main() { + t := T{n: 41} + sink = t + println(keepMethod(t)) +} diff --git a/cl/_testdrop/exported_method_crosspkg/api/api.go b/cl/_testdrop/exported_method_crosspkg/api/api.go new file mode 100644 index 0000000000..1080c2b72f --- /dev/null +++ b/cl/_testdrop/exported_method_crosspkg/api/api.go @@ -0,0 +1,9 @@ +package api + +type Keeper interface { + Keep() int +} + +func Use(k Keeper) int { + return k.Keep() +} diff --git a/cl/_testdrop/exported_method_crosspkg/expect.txt b/cl/_testdrop/exported_method_crosspkg/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/exported_method_crosspkg/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/exported_method_crosspkg/in.go b/cl/_testdrop/exported_method_crosspkg/in.go new file mode 100644 index 0000000000..76a7b84f54 --- /dev/null +++ b/cl/_testdrop/exported_method_crosspkg/in.go @@ -0,0 +1,29 @@ +// LITTEST +package main + +import "github.com/goplus/llgo/cl/_testdrop/exported_method_crosspkg/api" + +// SYMBOL-NOT: testdrop/exported_method_crosspkg{{.*}}T{{.*}}Drop +// SYMBOL-DAG: testdrop/exported_method_crosspkg{{.*}}T{{.*}}Keep +// SYMBOL-NOT: testdrop/exported_method_crosspkg{{.*}}T{{.*}}Drop + +// The interface type and dynamic call live in package api, while T and its +// method table live in this package. Exported method identity must let the +// global analysis match api.Keeper.Keep to T.Keep across package boundaries. +type T struct { + n int +} + +//go:noinline +func (t T) Keep() int { + return t.n + 1 +} + +//go:noinline +func (t T) Drop() int { + panic("Drop should be unreachable") +} + +func main() { + println(api.Use(T{n: 41})) +} diff --git a/cl/_testdrop/generic_interface_crosspkg/api/api.go b/cl/_testdrop/generic_interface_crosspkg/api/api.go new file mode 100644 index 0000000000..7e9c6a4ae1 --- /dev/null +++ b/cl/_testdrop/generic_interface_crosspkg/api/api.go @@ -0,0 +1,9 @@ +package api + +type I[T any] interface { + Value() T +} + +func UseInt(v I[int]) int { + return v.Value() +} diff --git a/cl/_testdrop/generic_interface_crosspkg/expect.txt b/cl/_testdrop/generic_interface_crosspkg/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/generic_interface_crosspkg/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/generic_interface_crosspkg/in.go b/cl/_testdrop/generic_interface_crosspkg/in.go new file mode 100644 index 0000000000..9190be00ff --- /dev/null +++ b/cl/_testdrop/generic_interface_crosspkg/in.go @@ -0,0 +1,30 @@ +// LITTEST +package main + +import ( + "github.com/goplus/llgo/cl/_testdrop/generic_interface_crosspkg/api" + "github.com/goplus/llgo/cl/_testdrop/generic_interface_crosspkg/model" +) + +// SYMBOL-NOT: testdrop/generic_interface_crosspkg/model{{.*}}Box{{.*}}int{{.*}}Drop +// SYMBOL-NOT: testdrop/generic_interface_crosspkg/model{{.*}}Box{{.*}}string{{.*}}Value +// SYMBOL-DAG: testdrop/generic_interface_crosspkg/model{{.*}}Box{{.*}}int{{.*}}Value +// SYMBOL-NOT: testdrop/generic_interface_crosspkg/model{{.*}}Box{{.*}}int{{.*}}Drop +// SYMBOL-NOT: testdrop/generic_interface_crosspkg/model{{.*}}Box{{.*}}string{{.*}}Value + +var sink any + +func main() { + // api.UseInt demands I[int].Value, so the instantiated interface method + // signature is Value() int, not Value() T. + n := api.UseInt(model.NewIntBox(40)) + + // Box[string] reaches interface-domain metadata through any, but the only + // reachable generic interface demand is I[int].Value. Box[string].Value has + // the same method name and a different instantiated signature, so it must + // not be retained by name alone. + text := model.NewStringBox("go") + sink = text + + println(n + model.UseStringBox(text)) +} diff --git a/cl/_testdrop/generic_interface_crosspkg/model/model.go b/cl/_testdrop/generic_interface_crosspkg/model/model.go new file mode 100644 index 0000000000..d9182b41f7 --- /dev/null +++ b/cl/_testdrop/generic_interface_crosspkg/model/model.go @@ -0,0 +1,27 @@ +package model + +type Box[T any] struct { + value T +} + +func NewIntBox(v int) *Box[int] { + return &Box[int]{value: v} +} + +func NewStringBox(v string) *Box[string] { + return &Box[string]{value: v} +} + +func UseStringBox(v *Box[string]) int { + return len(v.value) +} + +//go:noinline +func (b *Box[T]) Value() T { + return b.value +} + +//go:noinline +func (b *Box[T]) Drop() T { + panic("Box.Drop should be unreachable") +} diff --git a/cl/_testdrop/generic_interface_func_crosspkg/api/api.go b/cl/_testdrop/generic_interface_func_crosspkg/api/api.go new file mode 100644 index 0000000000..b5a2b8bf70 --- /dev/null +++ b/cl/_testdrop/generic_interface_func_crosspkg/api/api.go @@ -0,0 +1,9 @@ +package api + +type I[T any] interface { + Value() T +} + +func Use[T any](v I[T]) T { + return v.Value() +} diff --git a/cl/_testdrop/generic_interface_func_crosspkg/expect.txt b/cl/_testdrop/generic_interface_func_crosspkg/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/generic_interface_func_crosspkg/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/generic_interface_func_crosspkg/in.go b/cl/_testdrop/generic_interface_func_crosspkg/in.go new file mode 100644 index 0000000000..e4e82e4aeb --- /dev/null +++ b/cl/_testdrop/generic_interface_func_crosspkg/in.go @@ -0,0 +1,38 @@ +// LITTEST +package main + +import ( + "github.com/goplus/llgo/cl/_testdrop/generic_interface_func_crosspkg/api" + "github.com/goplus/llgo/cl/_testdrop/generic_interface_func_crosspkg/model" +) + +// SYMBOL-NOT: testdrop/generic_interface_func_crosspkg/model{{.*}}Box{{.*}}int{{.*}}Drop +// SYMBOL-NOT: testdrop/generic_interface_func_crosspkg/model{{.*}}Box{{.*}}uint{{.*}}Drop +// SYMBOL-NOT: testdrop/generic_interface_func_crosspkg/model{{.*}}Box{{.*}}string{{.*}}Value +// SYMBOL-DAG: testdrop/generic_interface_func_crosspkg/model{{.*}}Box{{.*}}int{{.*}}Value +// SYMBOL-DAG: testdrop/generic_interface_func_crosspkg/model{{.*}}Box{{.*}}uint{{.*}}Value +// SYMBOL-NOT: testdrop/generic_interface_func_crosspkg/model{{.*}}Box{{.*}}int{{.*}}Drop +// SYMBOL-NOT: testdrop/generic_interface_func_crosspkg/model{{.*}}Box{{.*}}uint{{.*}}Drop +// SYMBOL-NOT: testdrop/generic_interface_func_crosspkg/model{{.*}}Box{{.*}}string{{.*}}Value + +var sink any + +func main() { + // api.Use is generic, but this call instantiates it as Use[int]. The + // interface method demand inside Use is therefore I[int].Value, whose ABI + // method signature is Value() int. + n := api.Use[int](model.NewIntBox(40)) + + // This call relies on type argument inference. The compiler still + // instantiates api.Use as Use[uint], so it should produce an independent + // I[uint].Value demand. + u := api.Use(model.NewUintBox(1)) + + // Keep Box[string] metadata reachable without creating an I[string].Value + // demand. This guards the instantiated generic path against matching only + // by the method name Value. + text := model.NewStringBox("go") + sink = text + + println(n + int(u) + model.UseStringBox(text) - 1) +} diff --git a/cl/_testdrop/generic_interface_func_crosspkg/model/model.go b/cl/_testdrop/generic_interface_func_crosspkg/model/model.go new file mode 100644 index 0000000000..375164f214 --- /dev/null +++ b/cl/_testdrop/generic_interface_func_crosspkg/model/model.go @@ -0,0 +1,31 @@ +package model + +type Box[T any] struct { + value T +} + +func NewIntBox(v int) *Box[int] { + return &Box[int]{value: v} +} + +func NewUintBox(v uint) *Box[uint] { + return &Box[uint]{value: v} +} + +func NewStringBox(v string) *Box[string] { + return &Box[string]{value: v} +} + +func UseStringBox(v *Box[string]) int { + return len(v.value) +} + +//go:noinline +func (b *Box[T]) Value() T { + return b.value +} + +//go:noinline +func (b *Box[T]) Drop() T { + panic("Box.Drop should be unreachable") +} diff --git a/cl/_testdrop/iface_flow_crosspkg/api/api.go b/cl/_testdrop/iface_flow_crosspkg/api/api.go new file mode 100644 index 0000000000..1080c2b72f --- /dev/null +++ b/cl/_testdrop/iface_flow_crosspkg/api/api.go @@ -0,0 +1,9 @@ +package api + +type Keeper interface { + Keep() int +} + +func Use(k Keeper) int { + return k.Keep() +} diff --git a/cl/_testdrop/iface_flow_crosspkg/expect.txt b/cl/_testdrop/iface_flow_crosspkg/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/iface_flow_crosspkg/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/iface_flow_crosspkg/factory/factory.go b/cl/_testdrop/iface_flow_crosspkg/factory/factory.go new file mode 100644 index 0000000000..580c798553 --- /dev/null +++ b/cl/_testdrop/iface_flow_crosspkg/factory/factory.go @@ -0,0 +1,21 @@ +package factory + +import "github.com/goplus/llgo/cl/_testdrop/iface_flow_crosspkg/api" + +type T struct { + n int +} + +//go:noinline +func (t T) Keep() int { + return t.n + 1 +} + +//go:noinline +func (t T) Drop() int { + panic("Drop should be unreachable") +} + +func Make(n int) api.Keeper { + return T{n: n} +} diff --git a/cl/_testdrop/iface_flow_crosspkg/in.go b/cl/_testdrop/iface_flow_crosspkg/in.go new file mode 100644 index 0000000000..876e3220bf --- /dev/null +++ b/cl/_testdrop/iface_flow_crosspkg/in.go @@ -0,0 +1,18 @@ +// LITTEST +package main + +import ( + "github.com/goplus/llgo/cl/_testdrop/iface_flow_crosspkg/api" + "github.com/goplus/llgo/cl/_testdrop/iface_flow_crosspkg/factory" +) + +// SYMBOL-NOT: testdrop/iface_flow_crosspkg/factory{{.*}}T{{.*}}Drop +// SYMBOL-DAG: testdrop/iface_flow_crosspkg/factory{{.*}}T{{.*}}Keep +// SYMBOL-NOT: testdrop/iface_flow_crosspkg/factory{{.*}}T{{.*}}Drop + +func main() { + // factory.Make performs the concrete-to-interface conversion in another + // package. api.Use performs the interface method call in a third package. + // The global analysis must merge both facts before preserving T.Keep. + println(api.Use(factory.Make(41))) +} diff --git a/cl/_testdrop/interface_demand_fixedpoint/api/api.go b/cl/_testdrop/interface_demand_fixedpoint/api/api.go new file mode 100644 index 0000000000..d48ce376f6 --- /dev/null +++ b/cl/_testdrop/interface_demand_fixedpoint/api/api.go @@ -0,0 +1,28 @@ +package api + +type First interface { + Run() int +} + +type Second interface { + Next() int +} + +type Third interface { + Done() int +} + +//go:noinline +func UseFirst(f First) int { + return f.Run() +} + +//go:noinline +func UseSecond(s Second) int { + return s.Next() +} + +//go:noinline +func UseThird(t Third) int { + return t.Done() +} diff --git a/cl/_testdrop/interface_demand_fixedpoint/expect.txt b/cl/_testdrop/interface_demand_fixedpoint/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/interface_demand_fixedpoint/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/interface_demand_fixedpoint/flow/flow.go b/cl/_testdrop/interface_demand_fixedpoint/flow/flow.go new file mode 100644 index 0000000000..f7df2ef700 --- /dev/null +++ b/cl/_testdrop/interface_demand_fixedpoint/flow/flow.go @@ -0,0 +1,45 @@ +package flow + +import "github.com/goplus/llgo/cl/_testdrop/interface_demand_fixedpoint/api" + +//go:noinline +func Step(n int) int { + // This creates the api.Second.Next demand, but Step itself is reached only + // through the ordinary edge from dynamically kept Runner.Run. + return api.UseSecond(Worker{N: n}) +} + +type Worker struct { + N int +} + +//go:noinline +func (w Worker) Next() int { + // Keeping Worker.Next exposes another ordinary edge, which then reaches a + // third interface method demand. + return api.UseThird(Finisher{N: w.N}) +} + +//go:noinline +func (w Worker) Drop() int { + panic("Worker.Drop should be unreachable") +} + +type Finisher struct { + N int +} + +//go:noinline +func (f Finisher) Done() int { + return finalValue(f.N) +} + +//go:noinline +func (f Finisher) Drop() int { + panic("Finisher.Drop should be unreachable") +} + +//go:noinline +func finalValue(n int) int { + return n + 1 +} diff --git a/cl/_testdrop/interface_demand_fixedpoint/in.go b/cl/_testdrop/interface_demand_fixedpoint/in.go new file mode 100644 index 0000000000..cbd2030487 --- /dev/null +++ b/cl/_testdrop/interface_demand_fixedpoint/in.go @@ -0,0 +1,29 @@ +// LITTEST +package main + +import ( + "github.com/goplus/llgo/cl/_testdrop/interface_demand_fixedpoint/api" + "github.com/goplus/llgo/cl/_testdrop/interface_demand_fixedpoint/flow" + "github.com/goplus/llgo/cl/_testdrop/interface_demand_fixedpoint/model" +) + +// SYMBOL-NOT: testdrop/interface_demand_fixedpoint/model{{.*}}Runner{{.*}}Drop +// SYMBOL-NOT: testdrop/interface_demand_fixedpoint/flow{{.*}}Worker{{.*}}Drop +// SYMBOL-NOT: testdrop/interface_demand_fixedpoint/flow{{.*}}Finisher{{.*}}Drop +// SYMBOL-DAG: testdrop/interface_demand_fixedpoint/model{{.*}}Runner{{.*}}Run +// SYMBOL-DAG: testdrop/interface_demand_fixedpoint/flow{{.*}}Step +// SYMBOL-DAG: testdrop/interface_demand_fixedpoint/flow{{.*}}Worker{{.*}}Next +// SYMBOL-DAG: testdrop/interface_demand_fixedpoint/flow{{.*}}Finisher{{.*}}Done +// SYMBOL-NOT: testdrop/interface_demand_fixedpoint/model{{.*}}Runner{{.*}}Drop +// SYMBOL-NOT: testdrop/interface_demand_fixedpoint/flow{{.*}}Worker{{.*}}Drop +// SYMBOL-NOT: testdrop/interface_demand_fixedpoint/flow{{.*}}Finisher{{.*}}Drop + +var sink any + +func main() { + // Keep a second Worker type descriptor reachable without directly creating + // a Second.Next demand from main. The actual Worker.Next demand is produced + // only after Runner.Run is kept and reaches flow.Step. + sink = flow.Worker{N: 0} + println(api.UseFirst(model.NewRunner())) +} diff --git a/cl/_testdrop/interface_demand_fixedpoint/model/model.go b/cl/_testdrop/interface_demand_fixedpoint/model/model.go new file mode 100644 index 0000000000..95b48bc4ef --- /dev/null +++ b/cl/_testdrop/interface_demand_fixedpoint/model/model.go @@ -0,0 +1,21 @@ +package model + +import "github.com/goplus/llgo/cl/_testdrop/interface_demand_fixedpoint/flow" + +type Runner struct{} + +func NewRunner() Runner { + return Runner{} +} + +//go:noinline +func (Runner) Run() int { + // This static call is only reachable after Runner.Run is dynamically kept + // for the api.First.Run interface demand. + return flow.Step(41) +} + +//go:noinline +func (Runner) Drop() int { + panic("Runner.Drop should be unreachable") +} diff --git a/cl/_testdrop/interface_match/expect.txt b/cl/_testdrop/interface_match/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/interface_match/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/interface_match/in.go b/cl/_testdrop/interface_match/in.go new file mode 100644 index 0000000000..6c55a0c369 --- /dev/null +++ b/cl/_testdrop/interface_match/in.go @@ -0,0 +1,72 @@ +// LITTEST +package main + +// SYMBOL-NOT: testdrop/interface_match{{.*}}OnlyReader{{.*}}Read +// SYMBOL-NOT: testdrop/interface_match{{.*}}OnlyReader{{.*}}Drop +// SYMBOL-NOT: testdrop/interface_match{{.*}}Full{{.*}}Write +// SYMBOL-NOT: testdrop/interface_match{{.*}}Full{{.*}}Drop +// SYMBOL-DAG: testdrop/interface_match{{.*}}Full{{.*}}Read +// SYMBOL-NOT: testdrop/interface_match{{.*}}OnlyReader{{.*}}Read +// SYMBOL-NOT: testdrop/interface_match{{.*}}OnlyReader{{.*}}Drop +// SYMBOL-NOT: testdrop/interface_match{{.*}}Full{{.*}}Write +// SYMBOL-NOT: testdrop/interface_match{{.*}}Full{{.*}}Drop + +type Reader interface { + Read() int +} + +type ReadWriter interface { + Read() int + Write() int +} + +var sink any + +// OnlyReader has a Read method and reaches interface-typed metadata through +// any, but it does not implement ReadWriter. A ReadWriter.Read demand must not +// keep OnlyReader.Read alive by name alone. +type OnlyReader struct { + n int +} + +//go:noinline +func (r OnlyReader) Read() int { + return r.n + 1 +} + +//go:noinline +func (r OnlyReader) Drop() int { + panic("OnlyReader.Drop should be unreachable") +} + +// Full implements ReadWriter. The only reachable dynamic interface call is +// ReadWriter.Read, so Full.Read remains live while Full.Write and Full.Drop are +// not needed by the final method demand set. +type Full struct { + n int +} + +//go:noinline +func (f Full) Read() int { + return f.n + 10 +} + +//go:noinline +func (f Full) Write() int { + panic("Full.Write should be unreachable") +} + +//go:noinline +func (f Full) Drop() int { + panic("Full.Drop should be unreachable") +} + +func use(rw ReadWriter) int { + return rw.Read() +} + +func main() { + var _ Reader = OnlyReader{} + sink = OnlyReader{n: 1} + println(use(Full{n: 32})) +} diff --git a/cl/_testdrop/interface_slot/expect.txt b/cl/_testdrop/interface_slot/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/interface_slot/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/interface_slot/in.go b/cl/_testdrop/interface_slot/in.go new file mode 100644 index 0000000000..183adb1dac --- /dev/null +++ b/cl/_testdrop/interface_slot/in.go @@ -0,0 +1,35 @@ +// LITTEST +package main + +// SYMBOL-NOT: testdrop/interface_slot{{.*}}T{{.*}}Drop +// SYMBOL-DAG: testdrop/interface_slot{{.*}}T{{.*}}Keep +// SYMBOL-NOT: testdrop/interface_slot{{.*}}T{{.*}}Drop + +type I interface { + Keep() int +} + +// This case converts T to a non-empty interface and calls I.Keep. T implements +// I, so T.Keep must remain reachable, while T.Drop is not required by the +// reachable interface method demand. +type T struct { + n int +} + +//go:noinline +func (t T) Keep() int { + return t.n + 1 +} + +//go:noinline +func (t T) Drop() int { + panic("Drop should be unreachable") +} + +func use(i I) int { + return i.Keep() +} + +func main() { + println(use(T{n: 41})) +} diff --git a/cl/_testdrop/promoted_method_wrapper/expect.txt b/cl/_testdrop/promoted_method_wrapper/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/promoted_method_wrapper/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/promoted_method_wrapper/in.go b/cl/_testdrop/promoted_method_wrapper/in.go new file mode 100644 index 0000000000..8760bdecdd --- /dev/null +++ b/cl/_testdrop/promoted_method_wrapper/in.go @@ -0,0 +1,39 @@ +// LITTEST +package main + +// SYMBOL-NOT: testdrop/promoted_method_wrapper{{.*}}Wrapper{{.*}}Drop +// SYMBOL-DAG: testdrop/promoted_method_wrapper{{.*}}Wrapper{{.*}}Keep +// SYMBOL-NOT: testdrop/promoted_method_wrapper{{.*}}Wrapper{{.*}}Drop + +type I interface { + Keep() int +} + +type T struct { + n int +} + +//go:noinline +func (t *T) Keep() int { + return t.n + 1 +} + +//go:noinline +func (t *T) Drop() int { + panic("T.Drop should be unreachable") +} + +type Wrapper struct { + *T +} + +// This case converts Wrapper, not *T, to a non-empty interface. Wrapper has no +// declared Keep method; its method table slot is a promoted wrapper forwarding +// to (*T).Keep. DCE must keep that wrapper slot alive. +func use(i I) int { + return i.Keep() +} + +func main() { + println(use(Wrapper{T: &T{n: 41}})) +} diff --git a/cl/_testdrop/reflect_dynamic_iface_crosspkg/api/api.go b/cl/_testdrop/reflect_dynamic_iface_crosspkg/api/api.go new file mode 100644 index 0000000000..d51dcf51fe --- /dev/null +++ b/cl/_testdrop/reflect_dynamic_iface_crosspkg/api/api.go @@ -0,0 +1,12 @@ +package api + +type Reflector interface { + ReflectKeep() int +} + +var Sink Reflector + +//go:noinline +func Accept(r Reflector) { + Sink = r +} diff --git a/cl/_testdrop/reflect_dynamic_iface_crosspkg/expect.txt b/cl/_testdrop/reflect_dynamic_iface_crosspkg/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/reflect_dynamic_iface_crosspkg/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/reflect_dynamic_iface_crosspkg/in.go b/cl/_testdrop/reflect_dynamic_iface_crosspkg/in.go new file mode 100644 index 0000000000..ed0dac0d0d --- /dev/null +++ b/cl/_testdrop/reflect_dynamic_iface_crosspkg/in.go @@ -0,0 +1,33 @@ +// LITTEST +package main + +import ( + "reflect" + + "github.com/goplus/llgo/cl/_testdrop/reflect_dynamic_iface_crosspkg/api" + "github.com/goplus/llgo/cl/_testdrop/reflect_dynamic_iface_crosspkg/model" +) + +// SYMBOL-NOT: testdrop/reflect_dynamic_iface_crosspkg/model{{.*}}Unused{{.*}}ReflectKeep +// SYMBOL-DAG: testdrop/reflect_dynamic_iface_crosspkg/model{{.*}}Used{{.*}}ReflectKeep +// SYMBOL-NOT: testdrop/reflect_dynamic_iface_crosspkg/model{{.*}}Unused{{.*}}ReflectKeep + +//go:noinline +func dynamicName(name string) string { + return name +} + +func main() { + // api.Accept makes model.Used enter a reachable non-empty interface without + // calling ReflectKeep through that interface. The dynamic MethodByName below + // must conservatively keep exported methods for interface-domain types. + api.Accept(model.NewUsed(0)) + + // model.Unused also implements api.Reflector, but it is only used as a + // concrete value. It must not be retained just because a reachable interface + // has the same exported method. + unused := model.UseUnused(model.NewUnused(0)) + + out := reflect.ValueOf(model.NewUsed(41)).MethodByName(dynamicName("ReflectKeep")).Call(nil) + println(out[0].Int() + int64(unused)) +} diff --git a/cl/_testdrop/reflect_dynamic_iface_crosspkg/model/model.go b/cl/_testdrop/reflect_dynamic_iface_crosspkg/model/model.go new file mode 100644 index 0000000000..0836cd63ca --- /dev/null +++ b/cl/_testdrop/reflect_dynamic_iface_crosspkg/model/model.go @@ -0,0 +1,31 @@ +package model + +type Used struct { + n int +} + +func NewUsed(n int) Used { + return Used{n: n} +} + +//go:noinline +func (u Used) ReflectKeep() int { + return u.n + 1 +} + +type Unused struct { + n int +} + +func NewUnused(n int) Unused { + return Unused{n: n} +} + +//go:noinline +func (u Unused) ReflectKeep() int { + panic("Unused.ReflectKeep should be unreachable") +} + +func UseUnused(u Unused) int { + return u.n +} diff --git a/cl/_testdrop/reflect_named_method/expect.txt b/cl/_testdrop/reflect_named_method/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/reflect_named_method/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/reflect_named_method/in.go b/cl/_testdrop/reflect_named_method/in.go new file mode 100644 index 0000000000..59a2cbfd25 --- /dev/null +++ b/cl/_testdrop/reflect_named_method/in.go @@ -0,0 +1,28 @@ +// LITTEST +package main + +import "reflect" + +// SYMBOL-NOT: testdrop/reflect_named_method{{.*}}T{{.*}}Drop +// SYMBOL-DAG: testdrop/reflect_named_method{{.*}}T{{.*}}Keep +// SYMBOL-NOT: testdrop/reflect_named_method{{.*}}T{{.*}}Drop + +type T struct { + n int +} + +//go:noinline +func (t T) Keep() int { + return t.n + 1 +} + +//go:noinline +func (t T) Drop() int { + panic("Drop should be unreachable") +} + +func main() { + v := reflect.ValueOf(T{n: 41}) + out := v.MethodByName("Keep").Call(nil) + println(out[0].Int()) +} diff --git a/cl/_testdrop/source64_crosspkg/api/api.go b/cl/_testdrop/source64_crosspkg/api/api.go new file mode 100644 index 0000000000..9e125093e2 --- /dev/null +++ b/cl/_testdrop/source64_crosspkg/api/api.go @@ -0,0 +1,28 @@ +package api + +type Source interface { + Int63() int64 + Seed(int64) +} + +type Source64 interface { + Source + Uint64() uint64 +} + +type Rand struct { + src Source + s64 Source64 +} + +func New(src Source) *Rand { + s64, _ := src.(Source64) + return &Rand{src: src, s64: s64} +} + +func (r *Rand) Uint64() uint64 { + if r.s64 != nil { + return r.s64.Uint64() + } + return uint64(r.src.Int63()) +} diff --git a/cl/_testdrop/source64_crosspkg/expect.txt b/cl/_testdrop/source64_crosspkg/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/source64_crosspkg/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/source64_crosspkg/in.go b/cl/_testdrop/source64_crosspkg/in.go new file mode 100644 index 0000000000..8743c2646c --- /dev/null +++ b/cl/_testdrop/source64_crosspkg/in.go @@ -0,0 +1,31 @@ +// LITTEST +package main + +import ( + "github.com/goplus/llgo/cl/_testdrop/source64_crosspkg/api" + "github.com/goplus/llgo/cl/_testdrop/source64_crosspkg/model" +) + +// SYMBOL-NOT: testdrop/source64_crosspkg/model{{.*}}RuntimeSource{{.*}}Drop +// SYMBOL-NOT: testdrop/source64_crosspkg/model{{.*}}Uint64Only{{.*}}Uint64 +// SYMBOL-DAG: testdrop/source64_crosspkg/model{{.*}}RuntimeSource{{.*}}Uint64 +// SYMBOL-NOT: testdrop/source64_crosspkg/model{{.*}}RuntimeSource{{.*}}Drop +// SYMBOL-NOT: testdrop/source64_crosspkg/model{{.*}}Uint64Only{{.*}}Uint64 + +var sink any + +func main() { + // This mirrors math/rand's Source/Source64 shape without importing rand. + // RuntimeSource enters the interface domain as api.Source, then api.New + // discovers the wider api.Source64 interface and Rand.Uint64 performs the + // dynamic Source64.Uint64 call. + r := api.New(model.NewRuntimeSource(41)) + + // Uint64Only reaches type metadata through any and has the same Uint64 + // method name, but it does not fully implement api.Source64. The Source64 + // demand must not keep this method alive by name alone. + only := model.NewUint64Only(100) + sink = only + + println(r.Uint64() + model.UseUint64Only(only) - 100) +} diff --git a/cl/_testdrop/source64_crosspkg/model/model.go b/cl/_testdrop/source64_crosspkg/model/model.go new file mode 100644 index 0000000000..02dfe20578 --- /dev/null +++ b/cl/_testdrop/source64_crosspkg/model/model.go @@ -0,0 +1,48 @@ +package model + +import "github.com/goplus/llgo/cl/_testdrop/source64_crosspkg/api" + +type RuntimeSource struct { + n uint64 +} + +func NewRuntimeSource(n uint64) api.Source { + return &RuntimeSource{n: n} +} + +//go:noinline +func (s *RuntimeSource) Int63() int64 { + return int64(s.n) +} + +//go:noinline +func (s *RuntimeSource) Seed(seed int64) { + s.n = uint64(seed) +} + +//go:noinline +func (s *RuntimeSource) Uint64() uint64 { + return s.n + 1 +} + +//go:noinline +func (s *RuntimeSource) Drop() uint64 { + panic("RuntimeSource.Drop should be unreachable") +} + +type Uint64Only struct { + n uint64 +} + +func NewUint64Only(n uint64) Uint64Only { + return Uint64Only{n: n} +} + +func UseUint64Only(v Uint64Only) uint64 { + return v.n +} + +//go:noinline +func (v Uint64Only) Uint64() uint64 { + panic("Uint64Only.Uint64 should be unreachable") +} diff --git a/cl/_testdrop/unexported_method_identity/api/api.go b/cl/_testdrop/unexported_method_identity/api/api.go new file mode 100644 index 0000000000..9a030cada9 --- /dev/null +++ b/cl/_testdrop/unexported_method_identity/api/api.go @@ -0,0 +1,22 @@ +package api + +type hiddenIface interface { + hidden() int +} + +type Good struct { + n int +} + +//go:noinline +func (g Good) hidden() int { + return g.n + 1 +} + +func NewGood(n int) Good { + return Good{n: n} +} + +func Use(v hiddenIface) int { + return v.hidden() +} diff --git a/cl/_testdrop/unexported_method_identity/expect.txt b/cl/_testdrop/unexported_method_identity/expect.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/cl/_testdrop/unexported_method_identity/expect.txt @@ -0,0 +1 @@ +42 diff --git a/cl/_testdrop/unexported_method_identity/in.go b/cl/_testdrop/unexported_method_identity/in.go new file mode 100644 index 0000000000..39d9b7997f --- /dev/null +++ b/cl/_testdrop/unexported_method_identity/in.go @@ -0,0 +1,27 @@ +// LITTEST +package main + +import "github.com/goplus/llgo/cl/_testdrop/unexported_method_identity/api" + +// SYMBOL-NOT: testdrop/unexported_method_identity{{.*}}Local{{.*}}hidden +// SYMBOL-DAG: testdrop/unexported_method_identity/api{{.*}}Good{{.*}}hidden +// SYMBOL-NOT: testdrop/unexported_method_identity{{.*}}Local{{.*}}hidden + +var sink any + +// Local has a same-spelled unexported hidden method and its type metadata is +// reachable through any. The api.hiddenIface demand must not keep Local.hidden +// alive because unexported method identity includes the defining package. +type Local struct { + n int +} + +//go:noinline +func (l Local) hidden() int { + panic("Local.hidden should be unreachable") +} + +func main() { + sink = Local{n: 1} + println(api.Use(api.NewGood(41))) +} diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 5034f5e2f5..921cf8f2c5 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -88,7 +88,7 @@ func WithOutputFilter(filter func(string) string) RunOption { } } -// WithOutputCheck enables or disables runtime output golden checks in RunAndTestFromDir. +// WithOutputCheck enables or disables runtime output golden checks. func WithOutputCheck(enabled bool) RunOption { return func(opts *runOptions) { opts.checkOutput = enabled @@ -478,6 +478,20 @@ func testBuildAndCheckSymbolsFrom(t *testing.T, pkgDir, relPkg, sel, symbolSpec if sel != "" && !strings.Contains(pkgDir, sel) { return } + var ( + expectedOutput []byte + checkOutput bool + err error + ) + if opts.checkOutput { + expectedOutput, checkOutput, err = readGolden(filepath.Join(pkgDir, "expect.txt")) + if err != nil { + t.Fatal("ReadFile failed:", err) + } + if !checkOutput { + t.Fatal("missing expect.txt") + } + } outFile := filepath.Join(t.TempDir(), filepath.Base(pkgDir)) output, err := buildWithConf(relPkg, pkgDir, opts.conf, outFile) if err != nil { @@ -485,6 +499,25 @@ func testBuildAndCheckSymbolsFrom(t *testing.T, pkgDir, relPkg, sel, symbolSpec t.Fatalf("build failed: %v\noutput: %s", err, string(output)) } assertExpectedSymbols(t, outFile, symbolSpec) + if checkOutput { + output, err := runBuiltBinary(outFile, pkgDir) + if err != nil { + t.Logf("raw output:\n%s", string(output)) + t.Fatalf("run built binary failed: %v\noutput: %s", err, string(output)) + } + assertExpectedOutput(t, pkgDir, expectedOutput, output, opts) + } +} + +func runBuiltBinary(bin, dir string) ([]byte, error) { + cmd := exec.Command(bin) + cmd.Dir = dir + output, err := cmd.CombinedOutput() + output = filterRunOutput(output) + if err != nil { + return output, err + } + return output, nil } func buildWithConf(relPkg, pkgDir string, conf *build.Config, outFile string) ([]byte, error) { diff --git a/cl/compile_test.go b/cl/compile_test.go index dc2ae5ea3d..88b775ec20 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -223,6 +223,31 @@ func TestBuildAndCheckSymbolsFromTestlto(t *testing.T) { cltest.BuildAndCheckSymbolsFromDir(t, "", "./_testlto", testltoSymbolChecks, cltest.WithRunConfig(conf)) } +var testdropSymbolChecks = []string{ + "direct_func", + "direct_method", + "exported_method_crosspkg", + "generic_interface_crosspkg", + "generic_interface_func_crosspkg", + "iface_flow_crosspkg", + "interface_demand_fixedpoint", + "interface_match", + "interface_slot", + "promoted_method_wrapper", + "reflect_dynamic_iface_crosspkg", + "reflect_named_method", + "source64_crosspkg", + "unexported_method_identity", +} + +func TestBuildAndCheckSymbolsFromTestdrop(t *testing.T) { + conf := build.NewDefaultConf(build.ModeBuild) + cltest.BuildAndCheckSymbolsFromDir(t, "", "./_testdrop", testdropSymbolChecks, + cltest.WithRunConfig(conf), + cltest.WithOutputCheck(true), + ) +} + func TestFilterEmulatorOutput(t *testing.T) { tests := []struct { name string From fd8d8cb93b40a7b992b5ee4edcb92f3c83a6dff3 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Thu, 2 Jul 2026 21:02:31 +0800 Subject: [PATCH 30/37] meta: split function demands from ordinary edges --- internal/meta/build.go | 83 ++++++++++++----------- internal/meta/builder.go | 82 +++++++++++------------ internal/meta/format.go | 34 +++++----- internal/meta/global.go | 124 ++++++++++++++++++++--------------- internal/meta/global_test.go | 44 +++++++++++++ internal/meta/meta.go | 87 ++++++++++++++---------- internal/meta/meta_test.go | 72 +++++++++++--------- internal/meta/types.go | 30 ++++++--- 8 files changed, 330 insertions(+), 226 deletions(-) diff --git a/internal/meta/build.go b/internal/meta/build.go index 2b5446e6bd..e81f587a7a 100644 --- a/internal/meta/build.go +++ b/internal/meta/build.go @@ -21,11 +21,17 @@ func (b *Builder) Build() (*PackageMeta, error) { symSize := 4 + nsyms*12 // nsyms u32 + N×SymbolRecord(12) - totalEdges := uint32(0) - for _, es := range b.edges { - totalEdges += uint32(len(es)) + totalOrdinary := uint32(0) + for _, es := range b.ordinaryEdges { + totalOrdinary += uint32(len(es)) } - edgeSize := 4 + (nsyms+1)*4 + totalEdges*12 // nsyms + offsets[N+1] + N×Edge(12) + ordinarySize := 4 + (nsyms+1)*4 + totalOrdinary*4 // nsyms + offsets[N+1] + N×LocalSymbol(4) + + totalDemands := uint32(0) + for _, ds := range b.funcDemands { + totalDemands += uint32(len(ds)) + } + demandSize := 4 + (nsyms+1)*4 + totalDemands*12 // nsyms + offsets[N+1] + N×FuncDemand(12) totalChildren := uint32(0) for _, cs := range b.typeChildren { @@ -45,8 +51,6 @@ func (b *Builder) Build() (*PackageMeta, error) { } ifaceSize := 4 + (nsyms+1)*4 + totalSigs*12 // N×MethodSig(12: NameRef(8)+mtype) - reflSize := 4 + (nsyms+7)/8 // nsyms u32 + bitmap bytes - // ── 2. calculate section offsets ───────────────────────────────────────── var offsets [numSections]uint32 @@ -55,16 +59,16 @@ func (b *Builder) Build() (*PackageMeta, error) { cur += strSize offsets[SecSymbols] = cur cur += symSize - offsets[SecEdges] = cur - cur += edgeSize + offsets[SecOrdinaryEdges] = cur + cur += ordinarySize + offsets[SecFuncDemand] = cur + cur += demandSize offsets[SecTypeChildren] = cur cur += childSize offsets[SecMethodInfo] = cur cur += methodSize offsets[SecIfaceInfo] = cur cur += ifaceSize - offsets[SecReflect] = cur - cur += reflSize // ── 3. allocate one buffer ──────────────────────────────────────────────── @@ -82,11 +86,11 @@ func (b *Builder) Build() (*PackageMeta, error) { writeStringTable(raw[offsets[SecStringTable]:], b) writeSymbols(raw[offsets[SecSymbols]:], b, nsyms) - writeEdges(raw[offsets[SecEdges]:], b, nsyms) + writeOrdinaryEdges(raw[offsets[SecOrdinaryEdges]:], b, nsyms) + writeFuncDemand(raw[offsets[SecFuncDemand]:], b, nsyms) writeTypeChildren(raw[offsets[SecTypeChildren]:], b, nsyms) writeMethodInfo(raw[offsets[SecMethodInfo]:], b, nsyms) writeIfaceInfo(raw[offsets[SecIfaceInfo]:], b, nsyms) - writeReflect(raw[offsets[SecReflect]:], b, nsyms) return newPackageMeta(raw) } @@ -141,25 +145,44 @@ func writeCSROffsets(dst []byte, nsyms uint32, counts []int) []byte { return dst[4+(nsyms+1)*4:] } -// writeEdges writes the Edges section. +// writeOrdinaryEdges writes the OrdinaryEdges section. +// +// nsyms u32 +// offsets [nsyms+1] u32 +// data [] u32 (LocalSymbol) +func writeOrdinaryEdges(dst []byte, b *Builder, nsyms uint32) { + counts := make([]int, nsyms) + for i := range b.ordinaryEdges { + counts[i] = len(b.ordinaryEdges[i]) + } + data := writeCSROffsets(dst, nsyms, counts) + pos := 0 + for _, es := range b.ordinaryEdges { + for _, target := range es { + binary.LittleEndian.PutUint32(data[pos:], uint32(target)) + pos += 4 + } + } +} + +// writeFuncDemand writes the FuncDemand section. // // nsyms u32 // offsets [nsyms+1] u32 -// data [] { target u32, extra u32, kind u8, _ [3]byte } (12 bytes each) -func writeEdges(dst []byte, b *Builder, nsyms uint32) { +// data [] { kind u32, target u32, extra u32 } (12 bytes each) +func writeFuncDemand(dst []byte, b *Builder, nsyms uint32) { counts := make([]int, nsyms) - for i := range b.edges { - counts[i] = len(b.edges[i]) + for i := range b.funcDemands { + counts[i] = len(b.funcDemands[i]) } data := writeCSROffsets(dst, nsyms, counts) const rec = 12 pos := 0 - for _, es := range b.edges { - for _, e := range es { - binary.LittleEndian.PutUint32(data[pos:], e.target) - binary.LittleEndian.PutUint32(data[pos+4:], e.extra) - data[pos+8] = e.kind - // [pos+9 : pos+12] padding, zero + for _, ds := range b.funcDemands { + for _, d := range ds { + binary.LittleEndian.PutUint32(data[pos:], d.kind) + binary.LittleEndian.PutUint32(data[pos+4:], d.target) + binary.LittleEndian.PutUint32(data[pos+8:], d.extra) pos += rec } } @@ -232,17 +255,3 @@ func writeIfaceInfo(dst []byte, b *Builder, nsyms uint32) { } } } - -// writeReflect writes the ReflectBitmap section. -// -// nsyms u32 -// bitmap [(nsyms+7)/8] u8 -func writeReflect(dst []byte, b *Builder, nsyms uint32) { - binary.LittleEndian.PutUint32(dst, nsyms) - bm := dst[4:] - for i := LocalSymbol(0); i < LocalSymbol(nsyms); i++ { - if b.reflectBits.has(i) { - bm[i/8] |= 1 << (i % 8) - } - } -} diff --git a/internal/meta/builder.go b/internal/meta/builder.go index 59e8c268b4..2ca6f83171 100644 --- a/internal/meta/builder.go +++ b/internal/meta/builder.go @@ -1,30 +1,5 @@ package meta -// bitmap is a bit array backed by []uint32; each uint32 holds 32 bits. -// Indexed by LocalSymbol: bit i lives in [i/32] at position i%32. -// The slice length is always kept in sync with the symbol table by grow(), -// so set() and has() never need to check bounds. -type bitmap []uint32 - -// grow ensures the bitmap can hold bit index i. -// Called whenever a new symbol is registered. -func (bm *bitmap) grow(i LocalSymbol) { - need := uint(i)/32 + 1 - for uint(len(*bm)) < need { - *bm = append(*bm, 0) - } -} - -// set sets bit i. grow(i) must have been called beforehand. -func (bm bitmap) set(i LocalSymbol) { - bm[uint(i)/32] |= 1 << (uint(i) % 32) -} - -// has reports whether bit i is set. -func (bm bitmap) has(i LocalSymbol) bool { - return bm[uint(i)/32]&(1<<(uint(i)%32)) != 0 -} - // Builder accumulates per-package metadata facts and serializes them into // the binary wire format understood by PackageMeta. // @@ -44,8 +19,11 @@ type Builder struct { symNames []symEntry // indexed by LocalSymbol symMap map[string]LocalSymbol // name → LocalSymbol - // per-symbol edge lists (source LocalSymbol → edges) - edges [][]bEdge + // per-symbol ordinary edge lists (source LocalSymbol → target LocalSymbols) + ordinaryEdges [][]LocalSymbol + + // per-symbol function demand lists (source LocalSymbol → demand facts) + funcDemands [][]bFuncDemand // per-symbol TypeChildren lists typeChildren [][]LocalSymbol @@ -56,9 +34,6 @@ type Builder struct { // per-symbol InterfaceInfo (only interface types) ifaceInfo [][]bMethodSig - - // reflect bitmap: bit i set means symbol i triggers conservative reflection - reflectBits bitmap } type symEntry struct { @@ -66,10 +41,10 @@ type symEntry struct { nameLen uint32 } -type bEdge struct { - target uint32 // LocalSymbol or stringTable offset (UseNamedMethod) +type bFuncDemand struct { + kind uint32 + target uint32 // LocalSymbol or stringTable offset (DemandNamedMethod) extra uint32 - kind uint8 } type bMethodSlot struct { @@ -126,11 +101,11 @@ func (b *Builder) sym(name string) LocalSymbol { b.symNames = append(b.symNames, symEntry{nameOff: off, nameLen: uint32(len(name))}) b.symMap[name] = id // grow all per-symbol structures in sync with the symbol table - b.edges = append(b.edges, nil) + b.ordinaryEdges = append(b.ordinaryEdges, nil) + b.funcDemands = append(b.funcDemands, nil) b.typeChildren = append(b.typeChildren, nil) b.methodInfo = append(b.methodInfo, nil) b.ifaceInfo = append(b.ifaceInfo, nil) - b.reflectBits.grow(id) // ensure bit slot exists before any MarkReflect call return id } @@ -139,13 +114,30 @@ func (b *Builder) sym(name string) LocalSymbol { // - EdgeOrdinary: dst is a LocalSymbol; extra = 0 // - EdgeUseIface: dst is a LocalSymbol (type); extra = 0 // - EdgeUseIfaceMethod: dst is a LocalSymbol (interface); extra = method index -// - EdgeUseNamedMethod: dst is a string (method name); extra = 0 +// - EdgeUseNamedMethod: dst is a string-table offset; extra = string length func (b *Builder) AddEdge(src, dst LocalSymbol, kind uint8, extra uint32) { - b.edges[src] = append(b.edges[src], bEdge{ - target: uint32(dst), - extra: extra, - kind: kind, - }) + switch kind { + case EdgeOrdinary: + b.ordinaryEdges[src] = append(b.ordinaryEdges[src], dst) + case EdgeUseIface: + b.funcDemands[src] = append(b.funcDemands[src], bFuncDemand{ + kind: DemandUseIface, + target: uint32(dst), + extra: extra, + }) + case EdgeUseIfaceMethod: + b.funcDemands[src] = append(b.funcDemands[src], bFuncDemand{ + kind: DemandIfaceMethod, + target: uint32(dst), + extra: extra, + }) + case EdgeUseNamedMethod: + b.funcDemands[src] = append(b.funcDemands[src], bFuncDemand{ + kind: DemandNamedMethod, + target: uint32(dst), + extra: extra, + }) + } } // AddNamedMethodEdge records an EdgeUseNamedMethod edge where the target is a @@ -153,10 +145,10 @@ func (b *Builder) AddEdge(src, dst LocalSymbol, kind uint8, extra uint32) { // in target and its length in extra, together forming a NameRef. func (b *Builder) AddNamedMethodEdge(src LocalSymbol, methodName string) { ref := b.internName(methodName) - b.edges[src] = append(b.edges[src], bEdge{ + b.funcDemands[src] = append(b.funcDemands[src], bFuncDemand{ + kind: DemandNamedMethod, target: ref.Off, extra: ref.Len, - kind: EdgeUseNamedMethod, }) } @@ -201,5 +193,7 @@ func (b *Builder) AddIfaceMethod(iface LocalSymbol, methodName string, mtype Loc // MarkReflect marks sym as triggering conservative reflection handling. func (b *Builder) MarkReflect(sym LocalSymbol) { - b.reflectBits.set(sym) + b.funcDemands[sym] = append(b.funcDemands[sym], bFuncDemand{ + kind: DemandReflectMethod, + }) } diff --git a/internal/meta/format.go b/internal/meta/format.go index ab1fe627d4..bbc0682fed 100644 --- a/internal/meta/format.go +++ b/internal/meta/format.go @@ -32,26 +32,27 @@ func formatMeta(w *strings.Builder, pm *PackageMeta) { for i := LocalSymbol(0); i < LocalSymbol(n); i++ { src := symName(i) - for _, e := range pm.edges(i) { - switch e.Kind { - case EdgeOrdinary: - ordinary[src] = append(ordinary[src], symName(LocalSymbol(e.Target))) - case EdgeUseIface: - useIface[src] = append(useIface[src], symName(LocalSymbol(e.Target))) - case EdgeUseIfaceMethod: - ifaceSym := LocalSymbol(e.Target) + for _, dst := range pm.ordinaryEdges(i) { + ordinary[src] = append(ordinary[src], symName(dst)) + } + for _, d := range pm.funcDemands(i) { + switch d.Kind { + case DemandUseIface: + useIface[src] = append(useIface[src], symName(LocalSymbol(d.Target))) + case DemandIfaceMethod: + ifaceSym := LocalSymbol(d.Target) iface := symName(ifaceSym) sigs := pm.ifaceMethods(ifaceSym) - if int(e.Extra) < len(sigs) { - s := sigs[e.Extra] + if int(d.Extra) < len(sigs) { + s := sigs[d.Extra] useIfaceMethod[src] = append(useIfaceMethod[src], fmt.Sprintf("%s %s %s", iface, pm.nameString(s.Name), symName(s.MType))) } else { useIfaceMethod[src] = append(useIfaceMethod[src], - fmt.Sprintf("%s[%d]", iface, e.Extra)) + fmt.Sprintf("%s[%d]", iface, d.Extra)) } - case EdgeUseNamedMethod: - name := pm.nameString(NameRef{Off: e.Target, Len: e.Extra}) + case DemandNamedMethod: + name := pm.nameString(NameRef{Off: d.Target, Len: d.Extra}) useNamed[src] = append(useNamed[src], name) } } @@ -97,8 +98,11 @@ func formatMeta(w *strings.Builder, pm *PackageMeta) { // collect Reflect var reflectSyms []string for i := LocalSymbol(0); i < LocalSymbol(n); i++ { - if pm.hasReflect(i) { - reflectSyms = append(reflectSyms, symName(i)) + for _, d := range pm.funcDemands(i) { + if d.Kind == DemandReflectMethod { + reflectSyms = append(reflectSyms, symName(i)) + break + } } } diff --git a/internal/meta/global.go b/internal/meta/global.go index c7d1fef666..6b642da152 100644 --- a/internal/meta/global.go +++ b/internal/meta/global.go @@ -9,8 +9,10 @@ package meta // InterfaceMethods are NOT rewritten at merge time — they are translated // lazily on query. Only the strings are interned up front. // - Duplicate symbols (e.g. linkonce type descriptors emitted by several -// packages) are first-wins: the first package that owns facts for a symbol -// becomes its owner; later duplicates are ignored. +// packages) are assigned one owner by fact strength. Function-demand, +// MethodInfo, InterfaceInfo and TypeChildren facts outrank ordinary edges, +// so descriptor references do not hide the package that carries semantic +// method/interface facts. type GlobalSummary struct { pkgs []*PackageMeta @@ -19,6 +21,7 @@ type GlobalSummary struct { symStrings []string // Symbol → text locToGlb [][]Symbol // [pkgIdx][localSym] → global Symbol owner []symLoc // global Symbol → owning (pkg, local); pkg<0 if none + ownerRank []uint8 // method-name space (distinct from symbols) nameIntern map[string]Name @@ -26,7 +29,6 @@ type GlobalSummary struct { // per-type flags set at merge time (no translation, just CSR range checks) isInterface []bool // global Symbol → true if has iface methods - reflect map[Symbol]struct{} // lazily translated, cached on first access methodInfo map[Symbol][]GMethodSlot @@ -85,7 +87,6 @@ func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { locToGlb: make([][]Symbol, len(pkgs)), methodInfo: make(map[Symbol][]GMethodSlot), interfaceInfo: make(map[Symbol][]GMethodSig), - reflect: make(map[Symbol]struct{}), } // Phase 1: intern symbols, build locToGlb and owner, mark type kinds. @@ -99,8 +100,10 @@ func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { for li := LocalSymbol(0); li < LocalSymbol(n); li++ { gs := g.internSymbol(pm.symbolName(li)) tab[li] = gs - if g.owner[gs].pkg < 0 && hasFacts(pm, li) { + rank := ownerRank(pm, li) + if rank > g.ownerRank[gs] { g.owner[gs] = symLoc{pkg: int32(pi), local: li} + g.ownerRank[gs] = rank } // mark type kinds (no translation, just CSR range checks) @@ -108,23 +111,31 @@ func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { g.isInterface[gs] = true g.interfaces = append(g.interfaces, gs) } - if pm.hasReflect(li) { - g.reflect[gs] = struct{}{} - } } g.locToGlb[pi] = tab } return g, nil } -// hasFacts reports whether li carries any facts in pm (i.e. is defined here, -// not merely referenced). Used to pick the owning package for lazy queries. -func hasFacts(pm *PackageMeta, li LocalSymbol) bool { - return pm.hasEdges(li) || - pm.ntypeChild(li) > 0 || - pm.hasReflect(li) || - pm.nmethodSlot(li) > 0 || - pm.nifaceMethod(li) > 0 +// ownerRank reports whether li carries facts in pm and how authoritative those +// facts are for lazy global queries. Higher rank wins; equal rank keeps the +// first package, preserving deterministic first-wins behavior for equivalent +// duplicate ordinary/linkonce facts. +func ownerRank(pm *PackageMeta, li LocalSymbol) uint8 { + switch { + case pm.hasFuncDemand(li): + return 5 + case pm.nmethodSlot(li) > 0: + return 4 + case pm.nifaceMethod(li) > 0: + return 3 + case pm.ntypeChild(li) > 0: + return 2 + case pm.hasOrdinaryEdges(li): + return 1 + default: + return 0 + } } func (g *GlobalSummary) internSymbol(s string) Symbol { @@ -135,6 +146,7 @@ func (g *GlobalSummary) internSymbol(s string) Symbol { g.symIntern[s] = id g.symStrings = append(g.symStrings, s) g.owner = append(g.owner, symLoc{pkg: -1}) + g.ownerRank = append(g.ownerRank, 0) g.isInterface = append(g.isInterface, false) return id } @@ -250,45 +262,54 @@ func (g *GlobalSummary) InterfaceMethods(iface Symbol) []GMethodSig { // HasReflectMethod reports whether sym triggers conservative reflection handling. func (g *GlobalSummary) HasReflectMethod(sym Symbol) bool { - _, ok := g.reflect[sym] - return ok + _, _, demands := g.ownerDemands(sym) + for _, d := range demands { + if d.Kind == DemandReflectMethod { + return true + } + } + return false } // ── lazy edge queries ───────────────────────────────────────────────────────── -// ownerEdges returns the owning package, its locToGlb table, and the raw local -// edges for sym, or nil if sym has no owner. -func (g *GlobalSummary) ownerEdges(sym Symbol) (*PackageMeta, []Symbol, []Edge) { - if int(sym) >= len(g.owner) { - return nil, nil, nil +// ownerOrdinary returns the owning package's locToGlb table and raw local +// ordinary edges for sym, or nil if sym has no owner. +func (g *GlobalSummary) ownerOrdinary(sym Symbol) ([]Symbol, []LocalSymbol) { + pm, tab, li := g.ownerData(sym) + if pm == nil { + return nil, nil } - loc := g.owner[sym] - if loc.pkg < 0 { + return tab, pm.ordinaryEdges(li) +} + +// ownerDemands returns the owning package, its locToGlb table, and raw local +// FuncDemand records for sym, or nil if sym has no owner. +func (g *GlobalSummary) ownerDemands(sym Symbol) (*PackageMeta, []Symbol, []FuncDemand) { + pm, tab, li := g.ownerData(sym) + if pm == nil { return nil, nil, nil } - pm := g.pkgs[loc.pkg] - return pm, g.locToGlb[loc.pkg], pm.edges(loc.local) + return pm, tab, pm.funcDemands(li) } // OrdinaryEdges returns plain reachability targets from sym (global Symbols). func (g *GlobalSummary) OrdinaryEdges(sym Symbol) []Symbol { - _, tab, edges := g.ownerEdges(sym) + tab, edges := g.ownerOrdinary(sym) var out []Symbol - for _, e := range edges { - if e.Kind == EdgeOrdinary { - out = append(out, tab[e.Target]) - } + for _, dst := range edges { + out = append(out, tab[dst]) } return out } // UseIface returns concrete types converted to interfaces by sym. func (g *GlobalSummary) UseIface(sym Symbol) []Symbol { - _, tab, edges := g.ownerEdges(sym) + _, tab, demands := g.ownerDemands(sym) var out []Symbol - for _, e := range edges { - if e.Kind == EdgeUseIface { - out = append(out, tab[e.Target]) + for _, d := range demands { + if d.Kind == DemandUseIface { + out = append(out, tab[d.Target]) } } return out @@ -296,16 +317,16 @@ func (g *GlobalSummary) UseIface(sym Symbol) []Symbol { // UseIfaceMethod returns interface method demands emitted by sym. func (g *GlobalSummary) UseIfaceMethod(sym Symbol) []IfaceMethodDemand { - _, tab, edges := g.ownerEdges(sym) + _, tab, demands := g.ownerDemands(sym) var out []IfaceMethodDemand - for _, e := range edges { - if e.Kind != EdgeUseIfaceMethod { + for _, d := range demands { + if d.Kind != DemandIfaceMethod { continue } - iface := tab[e.Target] + iface := tab[d.Target] sigs := g.InterfaceMethods(iface) - if int(e.Extra) < len(sigs) { - out = append(out, IfaceMethodDemand{Target: iface, Sig: sigs[e.Extra]}) + if int(d.Extra) < len(sigs) { + out = append(out, IfaceMethodDemand{Target: iface, Sig: sigs[d.Extra]}) } } return out @@ -313,14 +334,14 @@ func (g *GlobalSummary) UseIfaceMethod(sym Symbol) []IfaceMethodDemand { // UseNamedMethod returns constant MethodByName names referenced by sym. func (g *GlobalSummary) UseNamedMethod(sym Symbol) []Name { - pm, _, edges := g.ownerEdges(sym) + pm, _, demands := g.ownerDemands(sym) if pm == nil { return nil } var out []Name - for _, e := range edges { - if e.Kind == EdgeUseNamedMethod { - name := pm.nameString(NameRef{Off: e.Target, Len: e.Extra}) + for _, d := range demands { + if d.Kind == DemandNamedMethod { + name := pm.nameString(NameRef{Off: d.Target, Len: d.Extra}) out = append(out, g.internName(name)) } } @@ -329,16 +350,11 @@ func (g *GlobalSummary) UseNamedMethod(sym Symbol) []Name { // TypeChildren returns child type symbols for typ (global Symbols). func (g *GlobalSummary) TypeChildren(typ Symbol) []Symbol { - if int(typ) >= len(g.owner) { - return nil - } - loc := g.owner[typ] - if loc.pkg < 0 { + pm, tab, li := g.ownerData(typ) + if pm == nil { return nil } - pm := g.pkgs[loc.pkg] - tab := g.locToGlb[loc.pkg] - local := pm.typeChildren(loc.local) + local := pm.typeChildren(li) if len(local) == 0 { return nil } diff --git a/internal/meta/global_test.go b/internal/meta/global_test.go index f8b6b87a1c..fb2c4fb22f 100644 --- a/internal/meta/global_test.go +++ b/internal/meta/global_test.go @@ -171,3 +171,47 @@ func TestGlobalSummaryLinkonce(t *testing.T) { t.Errorf("TypeChildren(foo) len = %d, want 1", got) } } + +func TestGlobalSummaryOwnerPrefersInterfaceInfoOverOrdinaryEdges(t *testing.T) { + descriptorPkg := func() *meta.PackageMeta { + b := meta.NewBuilder() + iface := b.Sym("_llgo_io.Reader") + dep := b.Sym("runtime.descriptor") + b.AddEdge(iface, dep, meta.EdgeOrdinary, 0) + pm, err := b.Build() + if err != nil { + t.Fatal(err) + } + return pm + }() + defPkg := func() *meta.PackageMeta { + b := meta.NewBuilder() + iface := b.Sym("_llgo_io.Reader") + readT := b.Sym("_llgo_func$Read") + b.AddIfaceMethod(iface, "Read", readT) + pm, err := b.Build() + if err != nil { + t.Fatal(err) + } + return pm + }() + defer descriptorPkg.Close() + defer defPkg.Close() + + g, err := meta.NewGlobalSummary([]*meta.PackageMeta{descriptorPkg, defPkg}) + if err != nil { + t.Fatalf("NewGlobalSummary: %v", err) + } + + iface, ok := g.LookupSymbol("_llgo_io.Reader") + if !ok { + t.Fatal("LookupSymbol(_llgo_io.Reader) not found") + } + methods := g.InterfaceMethods(iface) + if len(methods) != 1 { + t.Fatalf("InterfaceMethods(_llgo_io.Reader) len = %d, want 1", len(methods)) + } + if got := g.Name(methods[0].Name); got != "Read" { + t.Fatalf("InterfaceMethods(_llgo_io.Reader)[0].Name = %q, want Read", got) + } +} diff --git a/internal/meta/meta.go b/internal/meta/meta.go index 6319b5b15d..cd780afbcf 100644 --- a/internal/meta/meta.go +++ b/internal/meta/meta.go @@ -18,29 +18,29 @@ type PackageMeta struct { nsyms uint32 // cached section start offsets (parsed once from header) - strOff uint32 - symOff uint32 - edgeOff uint32 - childOff uint32 - methodOff uint32 - ifaceOff uint32 - reflOff uint32 -} - -// Edge is a decoded edge record returned by query methods. Its in-memory layout -// (Target@0, Extra@4, Kind@8, size 12) must match the on-disk wire layout exactly -// so Edges can reinterpret the mmap bytes as []Edge with no copy. -type Edge struct { - Target uint32 // LocalSymbol or stringTable offset (UseNamedMethod) + strOff uint32 + symOff uint32 + ordinaryOff uint32 + demandOff uint32 + childOff uint32 + methodOff uint32 + ifaceOff uint32 +} + +// FuncDemand is a decoded function-demand record. Its in-memory layout +// (Kind@0, Target@4, Extra@8, size 12) must match the on-disk wire layout exactly +// so funcDemands can reinterpret the mmap bytes as []FuncDemand with no copy. +type FuncDemand struct { + Kind uint32 + Target uint32 // LocalSymbol or stringTable offset (DemandNamedMethod) Extra uint32 - Kind uint8 } -// Compile-time assertion: Edge must be exactly 12 bytes. If either const goes -// negative the build fails, pinning the wire/struct layout match. +// Compile-time assertion: FuncDemand must be exactly 12 bytes. If either const +// goes negative the build fails, pinning the wire/struct layout match. const ( - _ = uint(unsafe.Sizeof(Edge{}) - 12) - _ = uint(12 - unsafe.Sizeof(Edge{})) + _ = uint(unsafe.Sizeof(FuncDemand{}) - 12) + _ = uint(12 - unsafe.Sizeof(FuncDemand{})) ) // MethodSlot is a decoded method slot record. Its layout (NameRef@0..8, @@ -136,15 +136,28 @@ func (pm *PackageMeta) nameString(ref NameRef) string { return unsafe.String(&pm.raw[pm.strOff+ref.Off], int(ref.Len)) } -// NEdge returns the number of outgoing edges from sym, or 0 if none. -func (pm *PackageMeta) nedge(sym LocalSymbol) uint32 { - s, e := pm.csrRange(pm.edgeOff, sym) +// NOrdinaryEdge returns the number of plain reachability edges from sym. +func (pm *PackageMeta) nordinaryEdge(sym LocalSymbol) uint32 { + s, e := pm.csrRange(pm.ordinaryOff, sym) return e - s } -// Edges returns all edges from sym as a zero-copy view into the mmap region. -func (pm *PackageMeta) edges(sym LocalSymbol) []Edge { - return csrSlice[Edge](pm, pm.edgeOff, sym, 12) +// ordinaryEdges returns all plain reachability targets from sym as a zero-copy +// view into the mmap region. +func (pm *PackageMeta) ordinaryEdges(sym LocalSymbol) []LocalSymbol { + return csrSlice[LocalSymbol](pm, pm.ordinaryOff, sym, 4) +} + +// NFuncDemand returns the number of method/interface/reflection demands from sym. +func (pm *PackageMeta) nfuncDemand(sym LocalSymbol) uint32 { + s, e := pm.csrRange(pm.demandOff, sym) + return e - s +} + +// funcDemands returns all method/interface/reflection demands from sym as a +// zero-copy view into the mmap region. +func (pm *PackageMeta) funcDemands(sym LocalSymbol) []FuncDemand { + return csrSlice[FuncDemand](pm, pm.demandOff, sym, 12) } // NTypeChild returns the number of type children for sym, or 0 if none. @@ -186,16 +199,22 @@ func (pm *PackageMeta) ifaceMethods(sym LocalSymbol) []MethodSig { // HasReflect reports whether sym triggers conservative reflection handling. func (pm *PackageMeta) hasReflect(sym LocalSymbol) bool { - if uint32(sym) >= pm.nsyms { - return false + for _, d := range pm.funcDemands(sym) { + if d.Kind == DemandReflectMethod { + return true + } } - bitmapBase := pm.reflOff + 4 - return pm.raw[bitmapBase+uint32(sym)/8]&(1<<(sym%8)) != 0 + return false +} + +// HasOrdinaryEdges reports whether sym has any plain reachability edges. +func (pm *PackageMeta) hasOrdinaryEdges(sym LocalSymbol) bool { + return pm.nordinaryEdge(sym) > 0 } -// HasEdges reports whether sym has any outgoing edges. -func (pm *PackageMeta) hasEdges(sym LocalSymbol) bool { - return pm.nedge(sym) > 0 +// HasFuncDemand reports whether sym has any method/interface/reflection demand. +func (pm *PackageMeta) hasFuncDemand(sym LocalSymbol) bool { + return pm.nfuncDemand(sym) > 0 } // ── internal helpers ────────────────────────────────────────────────────────── @@ -216,11 +235,11 @@ func newPackageMeta(raw []byte) (*PackageMeta, error) { pm := &PackageMeta{raw: raw} pm.strOff = binary.LittleEndian.Uint32(raw[8+SecStringTable*4:]) pm.symOff = binary.LittleEndian.Uint32(raw[8+SecSymbols*4:]) - pm.edgeOff = binary.LittleEndian.Uint32(raw[8+SecEdges*4:]) + pm.ordinaryOff = binary.LittleEndian.Uint32(raw[8+SecOrdinaryEdges*4:]) + pm.demandOff = binary.LittleEndian.Uint32(raw[8+SecFuncDemand*4:]) pm.childOff = binary.LittleEndian.Uint32(raw[8+SecTypeChildren*4:]) pm.methodOff = binary.LittleEndian.Uint32(raw[8+SecMethodInfo*4:]) pm.ifaceOff = binary.LittleEndian.Uint32(raw[8+SecIfaceInfo*4:]) - pm.reflOff = binary.LittleEndian.Uint32(raw[8+SecReflect*4:]) // read nsyms from Symbols section header pm.nsyms = binary.LittleEndian.Uint32(raw[pm.symOff:]) diff --git a/internal/meta/meta_test.go b/internal/meta/meta_test.go index 73f8471edc..ab332a25b9 100644 --- a/internal/meta/meta_test.go +++ b/internal/meta/meta_test.go @@ -10,17 +10,17 @@ import ( // correct total size and field offsets. If these drift, unsafe reinterpretation // of mmap bytes would silently corrupt — so we assert them explicitly. func TestWireLayout(t *testing.T) { - if got := unsafe.Sizeof(Edge{}); got != 12 { - t.Errorf("sizeof(Edge) = %d, want 12", got) + if got := unsafe.Sizeof(FuncDemand{}); got != 12 { + t.Errorf("sizeof(FuncDemand) = %d, want 12", got) } - if got := unsafe.Offsetof(Edge{}.Target); got != 0 { - t.Errorf("Edge.Target offset = %d, want 0", got) + if got := unsafe.Offsetof(FuncDemand{}.Kind); got != 0 { + t.Errorf("FuncDemand.Kind offset = %d, want 0", got) } - if got := unsafe.Offsetof(Edge{}.Extra); got != 4 { - t.Errorf("Edge.Extra offset = %d, want 4", got) + if got := unsafe.Offsetof(FuncDemand{}.Target); got != 4 { + t.Errorf("FuncDemand.Target offset = %d, want 4", got) } - if got := unsafe.Offsetof(Edge{}.Kind); got != 8 { - t.Errorf("Edge.Kind offset = %d, want 8", got) + if got := unsafe.Offsetof(FuncDemand{}.Extra); got != 8 { + t.Errorf("FuncDemand.Extra offset = %d, want 8", got) } if got := unsafe.Sizeof(MethodSlot{}); got != 20 { @@ -134,39 +134,47 @@ func TestRoundTrip(t *testing.T) { checkName(allocZ, "runtime.AllocZ") checkName(myType, "*_llgo_main.MyStruct") - // ── verify Edges ────────────────────────────────────────────────────────── + // ── verify OrdinaryEdges / FuncDemand ───────────────────────────────────── - mainEdges := pm.edges(main) - if len(mainEdges) != 4 { - t.Fatalf("Edges(main): got %d edges, want 4", len(mainEdges)) + mainEdges := pm.ordinaryEdges(main) + if len(mainEdges) != 2 { + t.Fatalf("OrdinaryEdges(main): got %d edges, want 2", len(mainEdges)) } - if e := mainEdges[0]; e.Kind != EdgeOrdinary || LocalSymbol(e.Target) != helper { - t.Errorf("edge[0] = %+v, want {Target:%d Kind:Ordinary}", e, helper) + if mainEdges[0] != helper { + t.Errorf("ordinary[0] = %d, want helper=%d", mainEdges[0], helper) } - if e := mainEdges[1]; e.Kind != EdgeOrdinary || LocalSymbol(e.Target) != allocZ { - t.Errorf("edge[1] = %+v, want {Target:%d Kind:Ordinary}", e, allocZ) + if mainEdges[1] != allocZ { + t.Errorf("ordinary[1] = %d, want allocZ=%d", mainEdges[1], allocZ) } - if e := mainEdges[2]; e.Kind != EdgeUseIface || LocalSymbol(e.Target) != myType { - t.Errorf("edge[2] = %+v, want {Target:%d Kind:UseIface}", e, myType) + + mainDemands := pm.funcDemands(main) + if len(mainDemands) != 2 { + t.Fatalf("FuncDemand(main): got %d demands, want 2", len(mainDemands)) + } + if d := mainDemands[0]; d.Kind != DemandUseIface || LocalSymbol(d.Target) != myType { + t.Errorf("demand[0] = %+v, want {Kind:UseIface Target:%d}", d, myType) } - if e := mainEdges[3]; e.Kind != EdgeUseIfaceMethod || LocalSymbol(e.Target) != myIface || e.Extra != 0 { - t.Errorf("edge[3] = %+v, want {Target:%d Kind:UseIfaceMethod Extra:0}", e, myIface) + if d := mainDemands[1]; d.Kind != DemandIfaceMethod || LocalSymbol(d.Target) != myIface || d.Extra != 0 { + t.Errorf("demand[1] = %+v, want {Kind:IfaceMethod Target:%d Extra:0}", d, myIface) } - helperEdges := pm.edges(helper) - if len(helperEdges) != 1 { - t.Fatalf("Edges(helper): got %d, want 1", len(helperEdges)) + helperDemands := pm.funcDemands(helper) + if len(helperDemands) != 2 { + t.Fatalf("FuncDemand(helper): got %d, want 2", len(helperDemands)) } - if e := helperEdges[0]; e.Kind != EdgeUseNamedMethod { - t.Errorf("helper edge[0].Kind = %d, want UseNamedMethod", e.Kind) + if d := helperDemands[0]; d.Kind != DemandNamedMethod { + t.Errorf("helper demand[0].Kind = %d, want NamedMethod", d.Kind) } // For UseNamedMethod, target=NameRef.Off and extra=NameRef.Len. - gotName := pm.nameString(NameRef{Off: helperEdges[0].Target, Len: helperEdges[0].Extra}) + gotName := pm.nameString(NameRef{Off: helperDemands[0].Target, Len: helperDemands[0].Extra}) if gotName != "ServeHTTP" { t.Errorf("UseNamedMethod target name = %q, want \"ServeHTTP\"", gotName) } - if got := pm.edges(allocZ); len(got) != 0 { - t.Errorf("Edges(allocZ): got %d, want 0", len(got)) + if d := helperDemands[1]; d.Kind != DemandReflectMethod { + t.Errorf("helper demand[1].Kind = %d, want ReflectMethod", d.Kind) + } + if got := pm.ordinaryEdges(allocZ); len(got) != 0 { + t.Errorf("OrdinaryEdges(allocZ): got %d, want 0", len(got)) } // ── verify TypeChildren ─────────────────────────────────────────────────── @@ -218,7 +226,7 @@ func TestRoundTrip(t *testing.T) { t.Errorf("NIfaceMethod(main) > 0, want 0") } - // ── verify ReflectBitmap ────────────────────────────────────────────────── + // ── verify reflect demand ───────────────────────────────────────────────── if !pm.hasReflect(helper) { t.Errorf("HasReflect(helper) = false, want true") @@ -254,8 +262,8 @@ func TestRoundTripFile(t *testing.T) { if got := pm2.symbolName(fn); got != "pkg.Fn" { t.Errorf("SymbolName after file round-trip = %q, want \"pkg.Fn\"", got) } - edges := pm2.edges(fn) - if len(edges) != 1 || LocalSymbol(edges[0].Target) != dep { - t.Errorf("Edges after file round-trip = %v", edges) + edges := pm2.ordinaryEdges(fn) + if len(edges) != 1 || edges[0] != dep { + t.Errorf("OrdinaryEdges after file round-trip = %v", edges) } } diff --git a/internal/meta/types.go b/internal/meta/types.go index e2d81001ca..5c69090d04 100644 --- a/internal/meta/types.go +++ b/internal/meta/types.go @@ -16,7 +16,9 @@ type NameRef struct { Len uint32 } -// Edge kinds used in the Edges section. +// Edge/demand kinds used by Builder.AddEdge for compatibility with existing +// emit sites. EdgeOrdinary is written to OrdinaryEdges; the other kinds are +// written to FuncDemand. const ( // EdgeOrdinary is a plain symbol reference (call, type use, global var, etc.). EdgeOrdinary uint8 = 0 @@ -29,22 +31,30 @@ const ( EdgeUseNamedMethod uint8 = 3 ) +// FuncDemand kinds used in the FuncDemand section. +const ( + DemandUseIface uint32 = uint32(EdgeUseIface) + DemandIfaceMethod uint32 = uint32(EdgeUseIfaceMethod) + DemandNamedMethod uint32 = uint32(EdgeUseNamedMethod) + DemandReflectMethod uint32 = 4 +) + // Magic is the 4-byte file signature. const Magic = "LLPM" // Version is the current binary format version. -const Version = 1 +const Version = 2 // Section index constants for Header.SectionOffsets. const ( - SecStringTable = 0 - SecSymbols = 1 - SecEdges = 2 - SecTypeChildren = 3 - SecMethodInfo = 4 - SecIfaceInfo = 5 - SecReflect = 6 - numSections = 7 + SecStringTable = 0 + SecSymbols = 1 + SecOrdinaryEdges = 2 + SecFuncDemand = 3 + SecTypeChildren = 4 + SecMethodInfo = 5 + SecIfaceInfo = 6 + numSections = 7 ) // headerSize = magic(4) + version(4) + sectionOffsets(numSections×4) From 9379516cf9de1922f47938898a80cc0fb82b218a Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Thu, 2 Jul 2026 22:13:47 +0800 Subject: [PATCH 31/37] meta: emit interface info at callsites --- .../interface_exported_var/meta-expect.txt | 4 +- .../interface_generic_crosspkg/api/api.go | 9 ++ .../api/meta-expect.txt | 13 ++ cl/_testmeta/interface_generic_crosspkg/in.go | 15 ++ .../meta-expect.txt | 139 ++++++++++++++++++ .../model/meta-expect.txt | 7 + .../interface_generic_crosspkg/model/model.go | 27 ++++ .../interface_imported/api/meta-expect.txt | 4 - .../interface_imported/meta-expect.txt | 6 +- cl/_testmeta/interface_named/meta-expect.txt | 2 - .../interface_unexported/meta-expect.txt | 2 - .../methodinfo_imported/meta-expect.txt | 6 - cl/_testmeta/reflect_named/meta-expect.txt | 44 +++++- cl/compile.go | 1 - cl/compile_test.go | 1 + ssa/abitype.go | 31 ---- ssa/interface.go | 1 + ssa/ssa_test.go | 8 +- 18 files changed, 262 insertions(+), 58 deletions(-) create mode 100644 cl/_testmeta/interface_generic_crosspkg/api/api.go create mode 100644 cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt create mode 100644 cl/_testmeta/interface_generic_crosspkg/in.go create mode 100644 cl/_testmeta/interface_generic_crosspkg/meta-expect.txt create mode 100644 cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt create mode 100644 cl/_testmeta/interface_generic_crosspkg/model/model.go diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt index cb2a43e790..457e68d525 100644 --- a/cl/_testmeta/interface_exported_var/meta-expect.txt +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -316,7 +316,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: - _llgo_encoding/binary.ByteOrder[4] + _llgo_encoding/binary.ByteOrder Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus [MethodInfo] *_llgo_encoding/binary.littleEndian: @@ -345,7 +345,7 @@ _llgo_encoding/binary.littleEndian: 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 [InterfaceInfo] -_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: +_llgo_encoding/binary.ByteOrder: PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 diff --git a/cl/_testmeta/interface_generic_crosspkg/api/api.go b/cl/_testmeta/interface_generic_crosspkg/api/api.go new file mode 100644 index 0000000000..7e9c6a4ae1 --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/api/api.go @@ -0,0 +1,9 @@ +package api + +type I[T any] interface { + Value() T +} + +func UseInt(v I[int]) int { + return v.Value() +} diff --git a/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt new file mode 100644 index 0000000000..27bcb84d10 --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt @@ -0,0 +1,13 @@ +[OrdinaryEdges] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.UseInt: + github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.init: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.init$guard + +[UseIfaceMethod] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.UseInt: + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + +[InterfaceInfo] +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA diff --git a/cl/_testmeta/interface_generic_crosspkg/in.go b/cl/_testmeta/interface_generic_crosspkg/in.go new file mode 100644 index 0000000000..bee45b9fbe --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/in.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api" + "github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model" +) + +var sink any + +func main() { + n := api.UseInt(model.NewIntBox(40)) + text := model.NewStringBox("go") + sink = text + println(n + model.UseStringBox(text)) +} diff --git a/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt new file mode 100644 index 0000000000..bbba9283ac --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt @@ -0,0 +1,139 @@ +[TypeChildren] +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] +*_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 +*_llgo_int: + _llgo_int +*_llgo_string: + _llgo_string +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + _llgo_int +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + _llgo_int +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + _llgo_string +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + +[OrdinaryEdges] +*_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] +*_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 +*_llgo_int: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_int +*_llgo_string: + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr + _llgo_string +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Value: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Value +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop +__llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal: + github.com/goplus/llgo/runtime/internal/runtime.interequal +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64: + github.com/goplus/llgo/runtime/internal/runtime.memequal64 +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequalptr: + github.com/goplus/llgo/runtime/internal/runtime.memequalptr +__llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal: + github.com/goplus/llgo/runtime/internal/runtime.strequal +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA: + *_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out +_llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA$out: + _llgo_int +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: + *_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out +_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to$out: + _llgo_string +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.struct$lOhriNu2BrWBR2Mh8k-KggMYlAw0Wx-2ftE2_gNyubg$fields + github.com/goplus/llgo/runtime/internal/runtime.structequal +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.struct$XNpHe-3-A25gBmHjYdF74H8M4k3Eik680huOzY6OtaU$fields + github.com/goplus/llgo/runtime/internal/runtime.structequal +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: + *_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.interequal + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods +_llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8$imethods: + _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA +_llgo_int: + *_llgo_int + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.memequal64 +_llgo_string: + *_llgo_string + __llgo_stub.github.com/goplus/llgo/runtime/internal/runtime.strequal +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.init: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.init$guard + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.init + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.init +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.main: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] + _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8 + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.sink + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.UseInt + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewIntBox + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewStringBox + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.UseStringBox + github.com/goplus/llgo/runtime/internal/runtime.NewItab + github.com/goplus/llgo/runtime/internal/runtime.PrintByte + github.com/goplus/llgo/runtime/internal/runtime.PrintInt +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop: + _llgo_string + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.Panic +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop: + _llgo_string + github.com/goplus/llgo/runtime/internal/runtime.AllocU + github.com/goplus/llgo/runtime/internal/runtime.Panic +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.struct$XNpHe-3-A25gBmHjYdF74H8M4k3Eik680huOzY6OtaU$fields: + _llgo_string +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.struct$lOhriNu2BrWBR2Mh8k-KggMYlAw0Wx-2ftE2_gNyubg$fields: + _llgo_int + +[UseIface] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg.main: + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int] + *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop: + _llgo_string +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop: + _llgo_string + +[MethodInfo] +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[int]: + 0 Drop _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Drop + 1 Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Value __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[int]).Value +*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: + 0 Drop _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop + 1 Value _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value diff --git a/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt new file mode 100644 index 0000000000..c2b2a46228 --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt @@ -0,0 +1,7 @@ +[OrdinaryEdges] +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewIntBox: + github.com/goplus/llgo/runtime/internal/runtime.AllocZ +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewStringBox: + github.com/goplus/llgo/runtime/internal/runtime.AllocZ +github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.init: + github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.init$guard diff --git a/cl/_testmeta/interface_generic_crosspkg/model/model.go b/cl/_testmeta/interface_generic_crosspkg/model/model.go new file mode 100644 index 0000000000..d9182b41f7 --- /dev/null +++ b/cl/_testmeta/interface_generic_crosspkg/model/model.go @@ -0,0 +1,27 @@ +package model + +type Box[T any] struct { + value T +} + +func NewIntBox(v int) *Box[int] { + return &Box[int]{value: v} +} + +func NewStringBox(v string) *Box[string] { + return &Box[string]{value: v} +} + +func UseStringBox(v *Box[string]) int { + return len(v.value) +} + +//go:noinline +func (b *Box[T]) Value() T { + return b.value +} + +//go:noinline +func (b *Box[T]) Drop() T { + panic("Box.Drop should be unreachable") +} diff --git a/cl/_testmeta/interface_imported/api/meta-expect.txt b/cl/_testmeta/interface_imported/api/meta-expect.txt index 301c517530..e30a53db60 100644 --- a/cl/_testmeta/interface_imported/api/meta-expect.txt +++ b/cl/_testmeta/interface_imported/api/meta-expect.txt @@ -10,7 +10,3 @@ github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read: github.com/goplus/llgo/cl/_testmeta/interface_imported/api.init: github.com/goplus/llgo/cl/_testmeta/interface_imported/api.init$guard -[InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Reader: - Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk - diff --git a/cl/_testmeta/interface_imported/meta-expect.txt b/cl/_testmeta/interface_imported/meta-expect.txt index d397a163d5..19b8845300 100644 --- a/cl/_testmeta/interface_imported/meta-expect.txt +++ b/cl/_testmeta/interface_imported/meta-expect.txt @@ -149,7 +149,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_imported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_imported.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Reader[0] + _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Reader Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: @@ -160,8 +160,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: 1 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read [InterfaceInfo] -_llgo_error: - Error _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to -_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: +_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Reader: Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index 6ee47f82d8..c9133a82ab 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -71,6 +71,4 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: [InterfaceInfo] _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: - M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index 67597ef3e0..0e8d32cc9c 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -71,6 +71,4 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: [InterfaceInfo] _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac -github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: - github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index 649cebb076..db25d4a6df 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -550,12 +550,6 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: 26 bytes.tryGrowByReslice _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M bytes.(*Buffer).tryGrowByReslice __llgo_stub.bytes.(*Buffer).tryGrowByReslice [InterfaceInfo] -_llgo_error: - Error _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to -_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: - Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk _llgo_io.Reader: Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk -_llgo_io.Writer: - Write _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index 54e2cbf315..187a7c38b4 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -52,8 +52,8 @@ github.com/goplus/llgo/cl/_testmeta/reflect_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: - _llgo_reflect.Type[21] - _llgo_reflect.Type[21] + _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E [UseNamedMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: @@ -68,3 +68,43 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.M 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m +[InterfaceInfo] +_llgo_reflect.Type: + Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + AssignableTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Bits _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + CanSeq _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + CanSeq2 _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ChanDir _llgo_func$JO3khPIbANSMBmoN6P7ybYAeUBd3Gv6toVUqNeE7qbE + Comparable _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + ConvertibleTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + Elem _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Field _llgo_func$Q3NYrysaKgu1MtMuLQwb-k5QcKGHihnt-tV_NlNJQFA + FieldAlign _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + FieldByIndex _llgo_func$LPPtiM49dEPl48CC3WRhXm3YPnfUJEZE_k8Tx3rMuSk + FieldByName _llgo_func$dEvABJ5r0MMUlf4smWpIDG5dO8AuGklGdNJ1xneL3UM + FieldByNameFunc _llgo_func$xySrXVFC_2LK2oP71R2UryKi6UmdEJUo9k6aQuz4TvI + Implements _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE + In _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + IsVariadic _llgo_func$YHeRw3AOvQtzv982-ZO3Yn8vh3Fx89RM3VvI8E4iKVk + Key _llgo_func$b6KOG2Oj7wt8ogb9H8QPbhEfXhxMMjdxRZgPLK_UOwI + Kind _llgo_func$w8Mj2LK8G5p7MIiGWR6MYjyXy3L8SVVzYlT1bb6KNXk + Len _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Method _llgo_func$FmJJGomlX5kINJGxQdQDCAkD89ySoMslAYFrziWInVc + MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + Name _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + NumField _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumIn _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumMethod _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + NumOut _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + Out _llgo_func$dPYu3A0LoGTV2Hd8PW4KPw2ITiUSo9q-4Bg9ZrPITnY + OverflowComplex _llgo_func$cGkbH-2LQOLoq64Rqj3WeO56U8al7FfVkf5K1FFbPpE + OverflowFloat _llgo_func$uk7PgUVap9GZdvS8R_mZCDbAbqnAbcNryqybtDogUNI + OverflowInt _llgo_func$odFOIClZoEVGbTP_BEfZxVM5ex3r8Fj1afUEeP_awp8 + OverflowUint _llgo_func$7I97sofX8UqJA96mVIy89KPUfSM_efkrR-mJQ9qaHfk + PkgPath _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + Size _llgo_func$1kITCsyu7hFLMxHLR7kDlvu4SOra_HtrtdFUQH9P13s + String _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to + reflect.common _llgo_func$w6XuV-1SmW103DbauPseXBpW50HpxXAEsUsGFibl0Uw + reflect.uncommon _llgo_func$iG49bujiXjI2lVflYdE0hPXlCAABL-XKRANSNJEKOio + diff --git a/cl/compile.go b/cl/compile.go index 956c09f76f..fe17d6a608 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -1759,7 +1759,6 @@ func newPackageEx(prog llssa.Program, patches Patches, rewrites map[string]strin if !ctx.skipall { processPkg(ctx, ret, pkg) } - ret.RecordDefinedInterfaceInfo(pkgTypes) for len(ctx.inits) > 0 { inits := ctx.inits ctx.inits = nil diff --git a/cl/compile_test.go b/cl/compile_test.go index 88b775ec20..622c69d194 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -242,6 +242,7 @@ var testdropSymbolChecks = []string{ func TestBuildAndCheckSymbolsFromTestdrop(t *testing.T) { conf := build.NewDefaultConf(build.ModeBuild) + conf.ForceRebuild = true cltest.BuildAndCheckSymbolsFromDir(t, "", "./_testdrop", testdropSymbolChecks, cltest.WithRunConfig(conf), cltest.WithOutputCheck(true), diff --git a/ssa/abitype.go b/ssa/abitype.go index 0f9d24ceca..17300f9573 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -205,7 +205,6 @@ func (b Builder) abiInterfaceImethods(t *types.Interface, typeName string) llvm. if n == 0 { return prog.Nil(prog.rtType("Slice")).impl } - b.Pkg.recordInterfaceInfo(t, typeName) imethodsName := typeName + "$imethods" g := b.Pkg.VarOf(imethodsName) @@ -239,36 +238,6 @@ func (b Builder) abiInterfaceImethods(t *types.Interface, typeName string) llvm. }) } -func (p Package) RecordDefinedInterfaceInfo(pkgTypes *types.Package) { - mb := p.MetaBuilder - if mb == nil || pkgTypes == nil { - return - } - scope := pkgTypes.Scope() - for _, name := range scope.Names() { - obj, ok := scope.Lookup(name).(*types.TypeName) - if !ok { - continue - } - if _, ok := obj.Type().(*types.Alias); ok { - continue - } - named, ok := types.Unalias(obj.Type()).(*types.Named) - if !ok { - continue - } - if tparams := named.TypeParams(); tparams != nil && tparams.Len() > 0 { - continue - } - iface, ok := named.Underlying().(*types.Interface) - if !ok || iface.NumMethods() == 0 { - continue - } - typeName, _ := p.Prog.abi.TypeName(named) - p.recordInterfaceInfo(iface, typeName) - } -} - func (p Package) recordInterfaceInfo(t *types.Interface, typeName string) { mb := p.MetaBuilder if mb == nil { diff --git a/ssa/interface.go b/ssa/interface.go index 5950ae70cc..3a79f118aa 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -94,6 +94,7 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { } intfSymName := func() string { n, _ := prog.abi.TypeName(intfSymType); return n }() intfSym := mb.Sym(intfSymName) + b.Pkg.recordInterfaceInfo(rawIntf, intfSymName) // Record which interface method is demanded. i is the method's index in rawIntf. mb.AddEdge(mb.Sym(b.Func.Name()), intfSym, meta.EdgeUseIfaceMethod, uint32(i)) } diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 7bc63ab9ca..dce9a1fc44 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -1207,11 +1207,11 @@ func TestIfaceMethodClosureCallIR(t *testing.T) { } defer pm.Close() gotMeta := pm.String() - if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt[0]") { - t.Fatalf("UseIfaceMethod should target named interface symbol, got:\n%s", gotMeta) + if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt Printf ") { + t.Fatalf("UseIfaceMethod should target named interface symbol and resolve its signature, got:\n%s", gotMeta) } - if strings.Contains(gotMeta, "_llgo_foo/bar.IFmt:\n Printf ") { - t.Fatalf("UseIfaceMethod callsite should not record InterfaceInfo, got:\n%s", gotMeta) + if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt:\n Printf ") { + t.Fatalf("UseIfaceMethod callsite should record InterfaceInfo for the same symbol, got:\n%s", gotMeta) } assertPkg(t, pkg, `; ModuleID = 'foo/bar' From ea939c3fa823e8a3e3a1d3f8a304063d3fe7f8ee Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 3 Jul 2026 10:29:27 +0800 Subject: [PATCH 32/37] test: fix generic interface meta golden newline --- cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt | 1 + cl/_testmeta/interface_generic_crosspkg/meta-expect.txt | 1 + cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt | 1 + 3 files changed, 3 insertions(+) diff --git a/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt index 27bcb84d10..69a0b8ba7a 100644 --- a/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt +++ b/cl/_testmeta/interface_generic_crosspkg/api/meta-expect.txt @@ -11,3 +11,4 @@ github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/api.UseInt: [InterfaceInfo] _llgo_iface$Jvxc0PCI_drlfK7S5npMGdZkQLeRkQ_x2e2CifPE6w8: Value _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA + diff --git a/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt index bbba9283ac..914b8ba2e0 100644 --- a/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt +++ b/cl/_testmeta/interface_generic_crosspkg/meta-expect.txt @@ -137,3 +137,4 @@ github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[strin *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.Box[string]: 0 Drop _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Drop 1 Value _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.(*Box[string]).Value + diff --git a/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt b/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt index c2b2a46228..094b8c759e 100644 --- a/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt +++ b/cl/_testmeta/interface_generic_crosspkg/model/meta-expect.txt @@ -5,3 +5,4 @@ github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.NewStringBo github.com/goplus/llgo/runtime/internal/runtime.AllocZ github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.init: github.com/goplus/llgo/cl/_testmeta/interface_generic_crosspkg/model.init$guard + From 6e761919c128146cc319025ed1b10482523bb7a7 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 3 Jul 2026 16:27:51 +0800 Subject: [PATCH 33/37] meta: remove legacy metadata package --- chore/metadump/main.go | 13 +- internal/metadata/builder.go | 139 --------- internal/metadata/decode.go | 314 -------------------- internal/metadata/encode.go | 137 --------- internal/metadata/format.go | 141 --------- internal/metadata/global_summary.go | 357 ----------------------- internal/metadata/global_summary_test.go | 158 ---------- internal/metadata/meta.go | 181 ------------ internal/metadata/metadata_test.go | 220 -------------- 9 files changed, 4 insertions(+), 1656 deletions(-) delete mode 100644 internal/metadata/builder.go delete mode 100644 internal/metadata/decode.go delete mode 100644 internal/metadata/encode.go delete mode 100644 internal/metadata/format.go delete mode 100644 internal/metadata/global_summary.go delete mode 100644 internal/metadata/global_summary_test.go delete mode 100644 internal/metadata/meta.go delete mode 100644 internal/metadata/metadata_test.go diff --git a/chore/metadump/main.go b/chore/metadump/main.go index c41e6c10e0..30ef950321 100644 --- a/chore/metadump/main.go +++ b/chore/metadump/main.go @@ -21,7 +21,7 @@ import ( "fmt" "os" - "github.com/goplus/llgo/internal/metadata" + "github.com/goplus/llgo/internal/meta" ) func main() { @@ -45,17 +45,12 @@ func main() { } func run(input, output string) error { - f, err := os.Open(input) - if err != nil { - return fmt.Errorf("open %s: %w", input, err) - } - defer f.Close() - - pm, err := metadata.ReadMeta(f) + pm, err := meta.ReadMeta(input) if err != nil { return fmt.Errorf("read %s: %w", input, err) } - text := metadata.MetaString(pm) + defer pm.Close() + text := pm.String() if output == "" { fmt.Print(text) diff --git a/internal/metadata/builder.go b/internal/metadata/builder.go deleted file mode 100644 index 4363486118..0000000000 --- a/internal/metadata/builder.go +++ /dev/null @@ -1,139 +0,0 @@ -package metadata - -// Builder accumulates per-package metadata facts and builds a PackageMeta. -type Builder struct { - pm *PackageMeta - strToID map[string]uint32 -} - -// NewBuilder creates an empty metadata builder. -func NewBuilder() *Builder { - return &Builder{ - pm: NewPackageMeta(nil), - strToID: make(map[string]uint32), - } -} - -// Symbol registers a module-level named symbol. -func (b *Builder) Symbol(s string) Symbol { - return Symbol(b.intern(s)) -} - -// Name registers a plain semantic name. -func (b *Builder) Name(s string) Name { - return Name(b.intern(s)) -} - -func (b *Builder) intern(s string) uint32 { - if id, ok := b.strToID[s]; ok { - return id - } - id := uint32(len(b.strToID)) - b.strToID[s] = id - return id -} - -// AddEdge records an ordinary reachability edge src -> dst. -func (b *Builder) AddEdge(src, dst Symbol) { - b.pm.ordinaryEdges[src] = appendSymbolUnique(b.pm.ordinaryEdges[src], dst) -} - -// AddTypeChild records that parent type references child type. -func (b *Builder) AddTypeChild(parent, child Symbol) { - b.pm.typeChildren[parent] = appendSymbolUnique(b.pm.typeChildren[parent], child) -} - -// AddIfaceEntry records the method set of an interface type. -func (b *Builder) AddIfaceEntry(iface Symbol, methods []MethodSig) { - for _, method := range methods { - b.pm.interfaceInfo[iface] = appendMethodSigUnique(b.pm.interfaceInfo[iface], method) - } -} - -// AddUseIface records types converted to interface when owner is reachable. -func (b *Builder) AddUseIface(owner Symbol, types []Symbol) { - for _, typ := range types { - b.pm.useIface[owner] = appendSymbolUnique(b.pm.useIface[owner], typ) - } -} - -// AddUseIfaceMethod records interface method calls when owner is reachable. -func (b *Builder) AddUseIfaceMethod(owner Symbol, demands []IfaceMethodDemand) { - for _, demand := range demands { - b.pm.useIfaceMethod[owner] = appendIfaceMethodDemandUnique(b.pm.useIfaceMethod[owner], demand) - } -} - -// AddMethodInfo records concrete type method table slots. -func (b *Builder) AddMethodInfo(typeID Symbol, slots []MethodSlot) { - for _, slot := range slots { - b.pm.methodInfo[typeID] = appendMethodSlotUnique(b.pm.methodInfo[typeID], slot) - } -} - -// AddUseNamedMethod records constant MethodByName method names. -func (b *Builder) AddUseNamedMethod(owner Symbol, names []Name) { - for _, name := range names { - b.pm.useNamedMethod[owner] = appendNameUnique(b.pm.useNamedMethod[owner], name) - } -} - -// AddReflectMethod records that owner triggers conservative reflection handling. -func (b *Builder) AddReflectMethod(owner Symbol) { - b.pm.reflectMethod[owner] = struct{}{} -} - -// Build finalizes the builder and returns the package metadata. -func (b *Builder) Build() *PackageMeta { - table := make([]string, len(b.strToID)) - for s, id := range b.strToID { - table[id] = s - } - b.pm.stringTable = table - return b.pm -} - -func appendSymbolUnique(items []Symbol, item Symbol) []Symbol { - for _, existing := range items { - if existing == item { - return items - } - } - return append(items, item) -} - -func appendNameUnique(items []Name, item Name) []Name { - for _, existing := range items { - if existing == item { - return items - } - } - return append(items, item) -} - -func appendMethodSigUnique(items []MethodSig, item MethodSig) []MethodSig { - for _, existing := range items { - if existing == item { - return items - } - } - return append(items, item) -} - -func appendIfaceMethodDemandUnique(items []IfaceMethodDemand, item IfaceMethodDemand) []IfaceMethodDemand { - for _, existing := range items { - if existing == item { - return items - } - } - return append(items, item) -} - -func appendMethodSlotUnique(items []MethodSlot, item MethodSlot) []MethodSlot { - for _, existing := range items { - if existing == item { - return items - } - } - return append(items, item) -} diff --git a/internal/metadata/decode.go b/internal/metadata/decode.go deleted file mode 100644 index d82f49f925..0000000000 --- a/internal/metadata/decode.go +++ /dev/null @@ -1,314 +0,0 @@ -package metadata - -import ( - "encoding/binary" - "fmt" - "io" - "math" -) - -// ReadMeta deserializes a PackageMeta from the LLPS v1 binary format. -func ReadMeta(r io.Reader) (*PackageMeta, error) { - data, err := io.ReadAll(r) - if err != nil { - return nil, fmt.Errorf("read meta: %w", err) - } - return decodeMeta(data) -} - -func decodeMeta(data []byte) (*PackageMeta, error) { - pos := 0 - - if len(data) < len(magicV1)+1 { - return nil, fmt.Errorf("data too short for header") - } - if magic := string(data[:len(magicV1)]); magic != magicV1 { - return nil, fmt.Errorf("bad magic: %q", magic) - } - pos += len(magicV1) - - version, err := readUvarint(data, &pos) - if err != nil { - return nil, fmt.Errorf("decode version: %w", err) - } - if version != version1 { - return nil, fmt.Errorf("unsupported version: %d", version) - } - - table, err := decodeStringTable(data, &pos) - if err != nil { - return nil, fmt.Errorf("stringTable: %w", err) - } - meta := NewPackageMeta(table) - - if err := decodeSymbolMap(data, &pos, meta.ordinaryEdges); err != nil { - return nil, fmt.Errorf("ordinaryEdges: %w", err) - } - if err := decodeSymbolMap(data, &pos, meta.typeChildren); err != nil { - return nil, fmt.Errorf("typeChildren: %w", err) - } - if err := decodeInterfaceInfo(data, &pos, meta.interfaceInfo); err != nil { - return nil, fmt.Errorf("interfaceInfo: %w", err) - } - if err := decodeSymbolMap(data, &pos, meta.useIface); err != nil { - return nil, fmt.Errorf("useIface: %w", err) - } - if err := decodeUseIfaceMethod(data, &pos, meta.useIfaceMethod); err != nil { - return nil, fmt.Errorf("useIfaceMethod: %w", err) - } - if err := decodeMethodInfo(data, &pos, meta.methodInfo); err != nil { - return nil, fmt.Errorf("methodInfo: %w", err) - } - if err := decodeUseNamedMethod(data, &pos, meta.useNamedMethod); err != nil { - return nil, fmt.Errorf("useNamedMethod: %w", err) - } - if err := decodeReflectMethod(data, &pos, meta.reflectMethod); err != nil { - return nil, fmt.Errorf("reflectMethod: %w", err) - } - if pos != len(data) { - return nil, fmt.Errorf("trailing data at pos %d", pos) - } - return meta, nil -} - -func decodeStringTable(data []byte, pos *int) ([]string, error) { - count, err := readUvarint(data, pos) - if err != nil { - return nil, fmt.Errorf("decode count: %w", err) - } - if count > uint64(math.MaxInt) { - return nil, fmt.Errorf("count overflows int: %d", count) - } - table := make([]string, int(count)) - for i := range table { - size, err := readUvarint(data, pos) - if err != nil { - return nil, fmt.Errorf("decode string %d len: %w", i, err) - } - if size > uint64(len(data)-*pos) { - return nil, fmt.Errorf("string %d out of range", i) - } - table[i] = string(data[*pos : *pos+int(size)]) - *pos += int(size) - } - return table, nil -} - -func readUvarint(data []byte, pos *int) (uint64, error) { - if *pos >= len(data) { - return 0, io.ErrUnexpectedEOF - } - value, n := binary.Uvarint(data[*pos:]) - if n == 0 { - return 0, io.ErrUnexpectedEOF - } - if n < 0 { - return 0, fmt.Errorf("uvarint overflow at pos %d", *pos) - } - *pos += n - return value, nil -} - -func readSymbol(data []byte, pos *int) (Symbol, error) { - value, err := readUvarint(data, pos) - if err != nil { - return 0, err - } - if value > math.MaxUint32 { - return 0, fmt.Errorf("symbol overflows uint32: %d", value) - } - return Symbol(value), nil -} - -func readName(data []byte, pos *int) (Name, error) { - value, err := readUvarint(data, pos) - if err != nil { - return 0, err - } - if value > math.MaxUint32 { - return 0, fmt.Errorf("name overflows uint32: %d", value) - } - return Name(value), nil -} - -func readCount(data []byte, pos *int, label string) (int, error) { - count, err := readUvarint(data, pos) - if err != nil { - return 0, err - } - if count > uint64(math.MaxInt) { - return 0, fmt.Errorf("%s count overflows int: %d", label, count) - } - return int(count), nil -} - -func decodeSymbolMap(data []byte, pos *int, m map[Symbol][]Symbol) error { - count, err := readCount(data, pos, "symbol map") - if err != nil { - return err - } - for range count { - key, err := readSymbol(data, pos) - if err != nil { - return err - } - nvalues, err := readCount(data, pos, "symbol values") - if err != nil { - return err - } - values := make([]Symbol, nvalues) - for i := range values { - values[i], err = readSymbol(data, pos) - if err != nil { - return err - } - } - m[key] = values - } - return nil -} - -func decodeInterfaceInfo(data []byte, pos *int, m map[Symbol][]MethodSig) error { - count, err := readCount(data, pos, "interface info") - if err != nil { - return err - } - for range count { - iface, err := readSymbol(data, pos) - if err != nil { - return err - } - nmethods, err := readCount(data, pos, "interface methods") - if err != nil { - return err - } - methods := make([]MethodSig, nmethods) - for i := range methods { - methods[i], err = readMethodSig(data, pos) - if err != nil { - return err - } - } - m[iface] = methods - } - return nil -} - -func readMethodSig(data []byte, pos *int) (MethodSig, error) { - name, err := readName(data, pos) - if err != nil { - return MethodSig{}, err - } - mtype, err := readSymbol(data, pos) - if err != nil { - return MethodSig{}, err - } - return MethodSig{Name: name, MType: mtype}, nil -} - -func decodeUseIfaceMethod(data []byte, pos *int, m map[Symbol][]IfaceMethodDemand) error { - count, err := readCount(data, pos, "use interface method") - if err != nil { - return err - } - for range count { - owner, err := readSymbol(data, pos) - if err != nil { - return err - } - ndemands, err := readCount(data, pos, "interface method demands") - if err != nil { - return err - } - demands := make([]IfaceMethodDemand, ndemands) - for i := range demands { - target, err := readSymbol(data, pos) - if err != nil { - return err - } - sig, err := readMethodSig(data, pos) - if err != nil { - return err - } - demands[i] = IfaceMethodDemand{Target: target, Sig: sig} - } - m[owner] = demands - } - return nil -} - -func decodeMethodInfo(data []byte, pos *int, m map[Symbol][]MethodSlot) error { - count, err := readCount(data, pos, "method info") - if err != nil { - return err - } - for range count { - typ, err := readSymbol(data, pos) - if err != nil { - return err - } - nslots, err := readCount(data, pos, "method slots") - if err != nil { - return err - } - slots := make([]MethodSlot, nslots) - for i := range slots { - sig, err := readMethodSig(data, pos) - if err != nil { - return err - } - ifn, err := readSymbol(data, pos) - if err != nil { - return err - } - tfn, err := readSymbol(data, pos) - if err != nil { - return err - } - slots[i] = MethodSlot{Sig: sig, IFn: ifn, TFn: tfn} - } - m[typ] = slots - } - return nil -} - -func decodeUseNamedMethod(data []byte, pos *int, m map[Symbol][]Name) error { - count, err := readCount(data, pos, "use named method") - if err != nil { - return err - } - for range count { - owner, err := readSymbol(data, pos) - if err != nil { - return err - } - nnames, err := readCount(data, pos, "method names") - if err != nil { - return err - } - names := make([]Name, nnames) - for i := range names { - names[i], err = readName(data, pos) - if err != nil { - return err - } - } - m[owner] = names - } - return nil -} - -func decodeReflectMethod(data []byte, pos *int, m map[Symbol]struct{}) error { - count, err := readCount(data, pos, "reflect method") - if err != nil { - return err - } - for range count { - owner, err := readSymbol(data, pos) - if err != nil { - return err - } - m[owner] = struct{}{} - } - return nil -} diff --git a/internal/metadata/encode.go b/internal/metadata/encode.go deleted file mode 100644 index 1d2feda808..0000000000 --- a/internal/metadata/encode.go +++ /dev/null @@ -1,137 +0,0 @@ -package metadata - -import ( - "encoding/binary" - "io" - "sort" -) - -const ( - magicV1 = "LLPS" - version1 = 1 -) - -// WriteMeta serializes PackageMeta to the LLPS v1 binary format. -func (pm *PackageMeta) WriteMeta(w io.Writer) error { - buf := make([]byte, 0, 4096) - - buf = append(buf, magicV1...) - buf = binary.AppendUvarint(buf, version1) - - buf = binary.AppendUvarint(buf, uint64(len(pm.stringTable))) - for _, s := range pm.stringTable { - buf = binary.AppendUvarint(buf, uint64(len(s))) - buf = append(buf, s...) - } - - writeSymbolMap(&buf, pm.ordinaryEdges) - writeSymbolMap(&buf, pm.typeChildren) - writeInterfaceInfo(&buf, pm.interfaceInfo) - writeSymbolMap(&buf, pm.useIface) - writeUseIfaceMethod(&buf, pm.useIfaceMethod) - writeMethodInfo(&buf, pm.methodInfo) - writeUseNamedMethod(&buf, pm.useNamedMethod) - writeReflectMethod(&buf, pm.reflectMethod) - - _, err := w.Write(buf) - return err -} - -func writeSymbolMap(buf *[]byte, m map[Symbol][]Symbol) { - keys := sortedSymbolKeys(m) - *buf = binary.AppendUvarint(*buf, uint64(len(keys))) - for _, key := range keys { - values := m[key] - *buf = binary.AppendUvarint(*buf, uint64(key)) - *buf = binary.AppendUvarint(*buf, uint64(len(values))) - for _, value := range values { - *buf = binary.AppendUvarint(*buf, uint64(value)) - } - } -} - -func writeInterfaceInfo(buf *[]byte, m map[Symbol][]MethodSig) { - keys := sortedSymbolKeys(m) - *buf = binary.AppendUvarint(*buf, uint64(len(keys))) - for _, iface := range keys { - methods := m[iface] - *buf = binary.AppendUvarint(*buf, uint64(iface)) - *buf = binary.AppendUvarint(*buf, uint64(len(methods))) - for _, method := range methods { - writeMethodSig(buf, method) - } - } -} - -func writeMethodSig(buf *[]byte, sig MethodSig) { - *buf = binary.AppendUvarint(*buf, uint64(sig.Name)) - *buf = binary.AppendUvarint(*buf, uint64(sig.MType)) -} - -func writeUseIfaceMethod(buf *[]byte, m map[Symbol][]IfaceMethodDemand) { - keys := sortedSymbolKeys(m) - *buf = binary.AppendUvarint(*buf, uint64(len(keys))) - for _, owner := range keys { - demands := m[owner] - *buf = binary.AppendUvarint(*buf, uint64(owner)) - *buf = binary.AppendUvarint(*buf, uint64(len(demands))) - for _, demand := range demands { - *buf = binary.AppendUvarint(*buf, uint64(demand.Target)) - writeMethodSig(buf, demand.Sig) - } - } -} - -func writeMethodInfo(buf *[]byte, m map[Symbol][]MethodSlot) { - keys := sortedSymbolKeys(m) - *buf = binary.AppendUvarint(*buf, uint64(len(keys))) - for _, typ := range keys { - slots := m[typ] - *buf = binary.AppendUvarint(*buf, uint64(typ)) - *buf = binary.AppendUvarint(*buf, uint64(len(slots))) - for _, slot := range slots { - writeMethodSig(buf, slot.Sig) - *buf = binary.AppendUvarint(*buf, uint64(slot.IFn)) - *buf = binary.AppendUvarint(*buf, uint64(slot.TFn)) - } - } -} - -func writeUseNamedMethod(buf *[]byte, m map[Symbol][]Name) { - keys := sortedSymbolKeys(m) - *buf = binary.AppendUvarint(*buf, uint64(len(keys))) - for _, owner := range keys { - names := m[owner] - *buf = binary.AppendUvarint(*buf, uint64(owner)) - *buf = binary.AppendUvarint(*buf, uint64(len(names))) - for _, name := range names { - *buf = binary.AppendUvarint(*buf, uint64(name)) - } - } -} - -func writeReflectMethod(buf *[]byte, m map[Symbol]struct{}) { - keys := make([]Symbol, 0, len(m)) - for key := range m { - keys = append(keys, key) - } - sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) - - *buf = binary.AppendUvarint(*buf, uint64(len(keys))) - for _, owner := range keys { - *buf = binary.AppendUvarint(*buf, uint64(owner)) - } -} - -type symbolValueMap[V any] interface { - ~map[Symbol]V -} - -func sortedSymbolKeys[M symbolValueMap[V], V any](m M) []Symbol { - keys := make([]Symbol, 0, len(m)) - for key := range m { - keys = append(keys, key) - } - sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) - return keys -} diff --git a/internal/metadata/format.go b/internal/metadata/format.go deleted file mode 100644 index 9939a76c4b..0000000000 --- a/internal/metadata/format.go +++ /dev/null @@ -1,141 +0,0 @@ -package metadata - -import ( - "fmt" - "io" - "sort" - "strings" -) - -// FormatMeta writes a stable human-readable representation for tests. -func FormatMeta(w io.Writer, pm *PackageMeta) { - if pm == nil { - return - } - - sym := func(s Symbol) string { - if int(s) < len(pm.stringTable) { - return pm.stringTable[s] - } - return fmt.Sprintf("?%d", s) - } - name := func(n Name) string { - if int(n) < len(pm.stringTable) { - return pm.stringTable[n] - } - return fmt.Sprintf("?%d", n) - } - keys := func(m map[Symbol][]Symbol) []Symbol { return sortedFormatKeys(m, sym) } - - if len(pm.typeChildren) > 0 { - fmt.Fprintln(w, "[TypeChildren]") - for _, key := range keys(pm.typeChildren) { - fmt.Fprintf(w, "%s:\n", sym(key)) - for _, child := range pm.typeChildren[key] { - fmt.Fprintf(w, " %s\n", sym(child)) - } - } - fmt.Fprintln(w) - } - - if len(pm.interfaceInfo) > 0 { - fmt.Fprintln(w, "[InterfaceInfo]") - for _, iface := range sortedFormatKeys(pm.interfaceInfo, sym) { - fmt.Fprintf(w, "%s:\n", sym(iface)) - for _, method := range pm.interfaceInfo[iface] { - fmt.Fprintf(w, " %s %s\n", name(method.Name), sym(method.MType)) - } - } - fmt.Fprintln(w) - } - - if len(pm.ordinaryEdges) > 0 { - fmt.Fprintln(w, "[OrdinaryEdges]") - for _, key := range keys(pm.ordinaryEdges) { - fmt.Fprintf(w, "%s:\n", sym(key)) - for _, dst := range pm.ordinaryEdges[key] { - fmt.Fprintf(w, " %s\n", sym(dst)) - } - } - fmt.Fprintln(w) - } - - if len(pm.useIface) > 0 { - fmt.Fprintln(w, "[UseIface]") - for _, key := range keys(pm.useIface) { - fmt.Fprintf(w, "%s:\n", sym(key)) - for _, typ := range pm.useIface[key] { - fmt.Fprintf(w, " %s\n", sym(typ)) - } - } - fmt.Fprintln(w) - } - - if len(pm.useIfaceMethod) > 0 { - fmt.Fprintln(w, "[UseIfaceMethod]") - for _, owner := range sortedFormatKeys(pm.useIfaceMethod, sym) { - fmt.Fprintf(w, "%s:\n", sym(owner)) - for _, demand := range pm.useIfaceMethod[owner] { - fmt.Fprintf(w, " %s %s %s\n", sym(demand.Target), name(demand.Sig.Name), sym(demand.Sig.MType)) - } - } - fmt.Fprintln(w) - } - - if len(pm.methodInfo) > 0 { - fmt.Fprintln(w, "[MethodInfo]") - for _, typ := range sortedFormatKeys(pm.methodInfo, sym) { - fmt.Fprintf(w, "%s:\n", sym(typ)) - for i, slot := range pm.methodInfo[typ] { - fmt.Fprintf(w, " %d %s %s %s %s\n", i, name(slot.Sig.Name), sym(slot.Sig.MType), sym(slot.IFn), sym(slot.TFn)) - } - } - fmt.Fprintln(w) - } - - if len(pm.useNamedMethod) > 0 { - fmt.Fprintln(w, "[UseNamedMethod]") - for _, owner := range sortedFormatKeys(pm.useNamedMethod, sym) { - fmt.Fprintf(w, "%s:\n", sym(owner)) - for _, methodName := range pm.useNamedMethod[owner] { - fmt.Fprintf(w, " %s\n", name(methodName)) - } - } - fmt.Fprintln(w) - } - - if len(pm.reflectMethod) > 0 { - owners := sortedFormatSetKeys(pm.reflectMethod, sym) - - fmt.Fprintln(w, "[ReflectMethod]") - for _, owner := range owners { - fmt.Fprintln(w, sym(owner)) - } - fmt.Fprintln(w) - } -} - -// MetaString returns the formatted metadata string. -func MetaString(pm *PackageMeta) string { - var sb strings.Builder - FormatMeta(&sb, pm) - return sb.String() -} - -func sortedFormatKeys[V any](m map[Symbol]V, name func(Symbol) string) []Symbol { - keys := make([]Symbol, 0, len(m)) - for key := range m { - keys = append(keys, key) - } - sort.Slice(keys, func(i, j int) bool { return name(keys[i]) < name(keys[j]) }) - return keys -} - -func sortedFormatSetKeys(m map[Symbol]struct{}, name func(Symbol) string) []Symbol { - keys := make([]Symbol, 0, len(m)) - for key := range m { - keys = append(keys, key) - } - sort.Slice(keys, func(i, j int) bool { return name(keys[i]) < name(keys[j]) }) - return keys -} diff --git a/internal/metadata/global_summary.go b/internal/metadata/global_summary.go deleted file mode 100644 index f0125e01c2..0000000000 --- a/internal/metadata/global_summary.go +++ /dev/null @@ -1,357 +0,0 @@ -package metadata - -import ( - "fmt" - "reflect" - "sort" -) - -// GlobalSummary is a whole-program metadata view in one global Symbol/Name space. -type GlobalSummary struct { - stringTable []string - - symbolByText map[string]Symbol - nameByText map[string]Name - - ordinaryEdges map[Symbol][]Symbol - typeChildren map[Symbol][]Symbol - interfaceInfo map[Symbol][]MethodSig - useIface map[Symbol][]Symbol - useIfaceMethod map[Symbol][]IfaceMethodDemand - methodInfo map[Symbol][]MethodSlot - useNamedMethod map[Symbol][]Name - reflectMethod map[Symbol]struct{} -} - -// NewGlobalSummary merges package-local metadata into a whole-program view. -func NewGlobalSummary(pkgs []*PackageMeta) (*GlobalSummary, error) { - b := newGlobalSummaryBuilder() - for _, pm := range pkgs { - if pm == nil { - continue - } - r := newPackageRemapper(pm, b) - - pm.ForEachOrdinaryEdge(func(src Symbol, dsts []Symbol) { - gsrc := r.symbol(src) - for _, dst := range dsts { - addUniqueSymbol(&b.summary.ordinaryEdges, gsrc, r.symbol(dst)) - } - }) - pm.ForEachTypeChild(func(parent Symbol, children []Symbol) { - gparent := r.symbol(parent) - for _, child := range children { - addUniqueSymbol(&b.summary.typeChildren, gparent, r.symbol(child)) - } - }) - pm.ForEachInterface(func(iface Symbol, methods []MethodSig) { - giface := r.symbol(iface) - for _, method := range methods { - addUniqueMethodSig(&b.summary.interfaceInfo, giface, r.methodSig(method)) - } - }) - pm.ForEachUseIface(func(owner Symbol, types []Symbol) { - gowner := r.symbol(owner) - for _, typ := range types { - addUniqueSymbol(&b.summary.useIface, gowner, r.symbol(typ)) - } - }) - pm.ForEachUseIfaceMethod(func(owner Symbol, demands []IfaceMethodDemand) { - gowner := r.symbol(owner) - for _, demand := range demands { - addUniqueIfaceMethodDemand(&b.summary.useIfaceMethod, gowner, IfaceMethodDemand{ - Target: r.symbol(demand.Target), - Sig: r.methodSig(demand.Sig), - }) - } - }) - var methodErr error - pm.ForEachMethodInfo(func(typ Symbol, slots []MethodSlot) { - if methodErr != nil { - return - } - gtyp := r.symbol(typ) - gslots := make([]MethodSlot, 0, len(slots)) - for _, slot := range slots { - gslots = append(gslots, MethodSlot{ - Sig: r.methodSig(slot.Sig), - IFn: r.symbol(slot.IFn), - TFn: r.symbol(slot.TFn), - }) - } - methodErr = b.addMethodSlots(gtyp, gslots) - }) - if methodErr != nil { - return nil, methodErr - } - pm.ForEachUseNamedMethod(func(owner Symbol, names []Name) { - gowner := r.symbol(owner) - for _, name := range names { - addUniqueName(&b.summary.useNamedMethod, gowner, r.name(name)) - } - }) - pm.ForEachReflectMethod(func(owner Symbol) { - b.summary.reflectMethod[r.symbol(owner)] = struct{}{} - }) - } - return b.build(), nil -} - -// LookupSymbol returns a global Symbol for a module-level symbol name. -func (g *GlobalSummary) LookupSymbol(name string) (Symbol, bool) { - if g == nil { - return 0, false - } - sym, ok := g.symbolByText[name] - return sym, ok -} - -// SymbolName returns the text referenced by a global Symbol. -func (g *GlobalSummary) SymbolName(sym Symbol) string { - if g == nil || int(sym) >= len(g.stringTable) { - return "" - } - return g.stringTable[sym] -} - -// Name returns the text referenced by a global Name. -func (g *GlobalSummary) Name(ref Name) string { - if g == nil || int(ref) >= len(g.stringTable) { - return "" - } - return g.stringTable[ref] -} - -// Interfaces returns all interface type symbols known to the summary. -func (g *GlobalSummary) Interfaces() []Symbol { - if g == nil { - return nil - } - return sortedGlobalKeys(g.interfaceInfo, g.SymbolName) -} - -// ConcreteTypes returns all concrete type symbols with method slots. -func (g *GlobalSummary) ConcreteTypes() []Symbol { - if g == nil { - return nil - } - return sortedGlobalKeys(g.methodInfo, g.SymbolName) -} - -// OrdinaryEdges returns direct ordinary references from sym. -func (g *GlobalSummary) OrdinaryEdges(sym Symbol) []Symbol { - if g == nil { - return nil - } - return cloneSymbols(g.ordinaryEdges[sym]) -} - -// TypeChildren returns child type symbols for typ. -func (g *GlobalSummary) TypeChildren(typ Symbol) []Symbol { - if g == nil { - return nil - } - return cloneSymbols(g.typeChildren[typ]) -} - -// InterfaceMethods returns the method set for iface. -func (g *GlobalSummary) InterfaceMethods(iface Symbol) []MethodSig { - if g == nil { - return nil - } - return cloneMethodSigs(g.interfaceInfo[iface]) -} - -// UseIface returns concrete types that enter interface semantics from fn. -func (g *GlobalSummary) UseIface(fn Symbol) []Symbol { - if g == nil { - return nil - } - return cloneSymbols(g.useIface[fn]) -} - -// UseIfaceMethod returns interface method demands emitted by fn. -func (g *GlobalSummary) UseIfaceMethod(fn Symbol) []IfaceMethodDemand { - if g == nil { - return nil - } - return cloneIfaceMethodDemands(g.useIfaceMethod[fn]) -} - -// MethodSlots returns ABI method slots for typ. -func (g *GlobalSummary) MethodSlots(typ Symbol) []MethodSlot { - if g == nil { - return nil - } - return cloneMethodSlots(g.methodInfo[typ]) -} - -// UseNamedMethod returns constant MethodByName names emitted by fn. -func (g *GlobalSummary) UseNamedMethod(fn Symbol) []Name { - if g == nil { - return nil - } - return cloneNames(g.useNamedMethod[fn]) -} - -// HasReflectMethod reports whether fn triggers conservative reflection handling. -func (g *GlobalSummary) HasReflectMethod(fn Symbol) bool { - if g == nil { - return false - } - _, ok := g.reflectMethod[fn] - return ok -} - -type globalSummaryBuilder struct { - summary *GlobalSummary - idByText map[string]uint32 -} - -func newGlobalSummaryBuilder() *globalSummaryBuilder { - return &globalSummaryBuilder{ - summary: &GlobalSummary{ - symbolByText: make(map[string]Symbol), - nameByText: make(map[string]Name), - ordinaryEdges: make(map[Symbol][]Symbol), - typeChildren: make(map[Symbol][]Symbol), - interfaceInfo: make(map[Symbol][]MethodSig), - useIface: make(map[Symbol][]Symbol), - useIfaceMethod: make(map[Symbol][]IfaceMethodDemand), - methodInfo: make(map[Symbol][]MethodSlot), - useNamedMethod: make(map[Symbol][]Name), - reflectMethod: make(map[Symbol]struct{}), - }, - idByText: make(map[string]uint32), - } -} - -func (b *globalSummaryBuilder) internSymbol(text string) Symbol { - id := b.internText(text) - sym := Symbol(id) - b.summary.symbolByText[text] = sym - return sym -} - -func (b *globalSummaryBuilder) internName(text string) Name { - id := b.internText(text) - name := Name(id) - b.summary.nameByText[text] = name - return name -} - -func (b *globalSummaryBuilder) internText(text string) uint32 { - if id, ok := b.idByText[text]; ok { - return id - } - id := uint32(len(b.summary.stringTable)) - b.idByText[text] = id - b.summary.stringTable = append(b.summary.stringTable, text) - return id -} - -func (b *globalSummaryBuilder) addMethodSlots(typ Symbol, slots []MethodSlot) error { - if existing, ok := b.summary.methodInfo[typ]; ok { - if reflect.DeepEqual(existing, slots) { - return nil - } - return fmt.Errorf("conflicting MethodInfo for %s", b.summary.SymbolName(typ)) - } - b.summary.methodInfo[typ] = cloneMethodSlots(slots) - return nil -} - -func (b *globalSummaryBuilder) build() *GlobalSummary { - return b.summary -} - -type packageRemapper struct { - pm *PackageMeta - b *globalSummaryBuilder - - symbols map[Symbol]Symbol - names map[Name]Name -} - -func newPackageRemapper(pm *PackageMeta, b *globalSummaryBuilder) *packageRemapper { - return &packageRemapper{ - pm: pm, - b: b, - symbols: make(map[Symbol]Symbol), - names: make(map[Name]Name), - } -} - -func (r *packageRemapper) symbol(local Symbol) Symbol { - if global, ok := r.symbols[local]; ok { - return global - } - global := r.b.internSymbol(r.pm.SymbolName(local)) - r.symbols[local] = global - return global -} - -func (r *packageRemapper) name(local Name) Name { - if global, ok := r.names[local]; ok { - return global - } - global := r.b.internName(r.pm.Name(local)) - r.names[local] = global - return global -} - -func (r *packageRemapper) methodSig(local MethodSig) MethodSig { - return MethodSig{ - Name: r.name(local.Name), - MType: r.symbol(local.MType), - } -} - -func addUniqueSymbol(m *map[Symbol][]Symbol, key, value Symbol) { - values := (*m)[key] - for _, existing := range values { - if existing == value { - return - } - } - (*m)[key] = append(values, value) -} - -func addUniqueName(m *map[Symbol][]Name, key Symbol, value Name) { - values := (*m)[key] - for _, existing := range values { - if existing == value { - return - } - } - (*m)[key] = append(values, value) -} - -func addUniqueMethodSig(m *map[Symbol][]MethodSig, key Symbol, value MethodSig) { - values := (*m)[key] - for _, existing := range values { - if existing == value { - return - } - } - (*m)[key] = append(values, value) -} - -func addUniqueIfaceMethodDemand(m *map[Symbol][]IfaceMethodDemand, key Symbol, value IfaceMethodDemand) { - values := (*m)[key] - for _, existing := range values { - if existing == value { - return - } - } - (*m)[key] = append(values, value) -} - -func sortedGlobalKeys[V any](m map[Symbol]V, name func(Symbol) string) []Symbol { - keys := make([]Symbol, 0, len(m)) - for key := range m { - keys = append(keys, key) - } - sort.Slice(keys, func(i, j int) bool { return name(keys[i]) < name(keys[j]) }) - return keys -} diff --git a/internal/metadata/global_summary_test.go b/internal/metadata/global_summary_test.go deleted file mode 100644 index ce3f5093e8..0000000000 --- a/internal/metadata/global_summary_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package metadata - -import ( - "reflect" - "testing" -) - -func TestGlobalSummaryMergesLocalIDsByText(t *testing.T) { - pkgA, refsA := buildGlobalSummaryPkgA() - pkgB, refsB := buildGlobalSummaryPkgB() - - if refsA.intType == refsB.intType { - t.Fatal("test setup should use different local Symbol IDs for _llgo_int") - } - - summary, err := NewGlobalSummary([]*PackageMeta{pkgA, pkgB}) - if err != nil { - t.Fatalf("NewGlobalSummary: %v", err) - } - - intSymA, ok := summary.LookupSymbol("_llgo_int") - if !ok { - t.Fatal("LookupSymbol(_llgo_int) failed") - } - if got := summary.SymbolName(intSymA); got != "_llgo_int" { - t.Fatalf("SymbolName(_llgo_int) = %q", got) - } - - funcType, ok := summary.LookupSymbol("_llgo_func$X") - if !ok { - t.Fatal("LookupSymbol(_llgo_func$X) failed") - } - mainA := mustLookupSymbol(t, summary, "pkg/a.main") - useA := mustLookupSymbol(t, summary, "pkg/a.use") - typeB := mustLookupSymbol(t, summary, "_llgo_pkg/b.T") - ifaceB := mustLookupSymbol(t, summary, "_llgo_iface$B") - ifnB := mustLookupSymbol(t, summary, "pkg/b.(*T).M") - tfnB := mustLookupSymbol(t, summary, "pkg/b.T.M") - - if got := summary.OrdinaryEdges(mainA); !reflect.DeepEqual(got, []Symbol{useA}) { - t.Fatalf("OrdinaryEdges(pkg/a.main) = %#v, want %#v", got, []Symbol{useA}) - } - if got := summary.TypeChildren(typeB); !reflect.DeepEqual(got, []Symbol{intSymA}) { - t.Fatalf("TypeChildren(_llgo_pkg/b.T) = %#v, want %#v", got, []Symbol{intSymA}) - } - if got := summary.UseIface(mainA); !reflect.DeepEqual(got, []Symbol{typeB}) { - t.Fatalf("UseIface(pkg/a.main) = %#v, want %#v", got, []Symbol{typeB}) - } - - methodName := summary.UseNamedMethod(useA) - if len(methodName) != 1 || summary.Name(methodName[0]) != "M" { - t.Fatalf("UseNamedMethod(pkg/a.use) = %#v, want name M", methodName) - } - if _, ok := summary.LookupSymbol("M"); ok { - t.Fatal("LookupSymbol found method name that only appeared as Name") - } - - wantSig := MethodSig{Name: methodName[0], MType: funcType} - if got := summary.InterfaceMethods(ifaceB); !reflect.DeepEqual(got, []MethodSig{wantSig}) { - t.Fatalf("InterfaceMethods(_llgo_iface$B) = %#v, want %#v", got, []MethodSig{wantSig}) - } - if got := summary.UseIfaceMethod(useA); !reflect.DeepEqual(got, []IfaceMethodDemand{{Target: ifaceB, Sig: wantSig}}) { - t.Fatalf("UseIfaceMethod(pkg/a.use) = %#v", got) - } - if got := summary.MethodSlots(typeB); !reflect.DeepEqual(got, []MethodSlot{{Sig: wantSig, IFn: ifnB, TFn: tfnB}}) { - t.Fatalf("MethodSlots(_llgo_pkg/b.T) = %#v", got) - } - if !summary.HasReflectMethod(useA) { - t.Fatal("HasReflectMethod(pkg/a.use) = false, want true") - } - - if got := summary.Interfaces(); !reflect.DeepEqual(got, []Symbol{ifaceB}) { - t.Fatalf("Interfaces() = %#v, want %#v", got, []Symbol{ifaceB}) - } - if got := summary.ConcreteTypes(); !reflect.DeepEqual(got, []Symbol{typeB}) { - t.Fatalf("ConcreteTypes() = %#v, want %#v", got, []Symbol{typeB}) - } -} - -func TestGlobalSummaryRejectsConflictingMethodSlots(t *testing.T) { - pkgA, _ := buildGlobalSummaryPkgB() - - // Rebuild pkgB with the same type and slot signature but a different TFn. - b := NewBuilder() - typ := b.Symbol("_llgo_pkg/b.T") - methodName := b.Name("M") - funcType := b.Symbol("_llgo_func$X") - ifn := b.Symbol("pkg/b.(*T).M") - tfn := b.Symbol("pkg/b.T.M.conflict") - b.AddMethodInfo(typ, []MethodSlot{{ - Sig: MethodSig{Name: methodName, MType: funcType}, - IFn: ifn, - TFn: tfn, - }}) - pkgB := b.Build() - - if _, err := NewGlobalSummary([]*PackageMeta{pkgA, pkgB}); err == nil { - t.Fatal("NewGlobalSummary accepted conflicting MethodInfo for the same type") - } -} - -type globalSummaryRefs struct { - intType Symbol -} - -func buildGlobalSummaryPkgA() (*PackageMeta, globalSummaryRefs) { - b := NewBuilder() - main := b.Symbol("pkg/a.main") - use := b.Symbol("pkg/a.use") - methodName := b.Name("M") - typ := b.Symbol("_llgo_pkg/b.T") - iface := b.Symbol("_llgo_iface$B") - funcType := b.Symbol("_llgo_func$X") - intType := b.Symbol("_llgo_int") - - b.AddEdge(main, use) - b.AddUseIface(main, []Symbol{typ}) - b.AddUseIfaceMethod(use, []IfaceMethodDemand{{ - Target: iface, - Sig: MethodSig{Name: methodName, MType: funcType}, - }}) - b.AddUseNamedMethod(use, []Name{methodName}) - b.AddReflectMethod(use) - - return b.Build(), globalSummaryRefs{intType: intType} -} - -func buildGlobalSummaryPkgB() (*PackageMeta, globalSummaryRefs) { - b := NewBuilder() - other := b.Symbol("pkg/b.other") - intType := b.Symbol("_llgo_int") - typ := b.Symbol("_llgo_pkg/b.T") - methodName := b.Name("M") - funcType := b.Symbol("_llgo_func$X") - iface := b.Symbol("_llgo_iface$B") - ifn := b.Symbol("pkg/b.(*T).M") - tfn := b.Symbol("pkg/b.T.M") - - b.AddEdge(other, intType) - b.AddTypeChild(typ, intType) - b.AddIfaceEntry(iface, []MethodSig{{Name: methodName, MType: funcType}}) - b.AddMethodInfo(typ, []MethodSlot{{ - Sig: MethodSig{Name: methodName, MType: funcType}, - IFn: ifn, - TFn: tfn, - }}) - - return b.Build(), globalSummaryRefs{intType: intType} -} - -func mustLookupSymbol(t *testing.T, summary *GlobalSummary, name string) Symbol { - t.Helper() - sym, ok := summary.LookupSymbol(name) - if !ok { - t.Fatalf("LookupSymbol(%q) failed", name) - } - return sym -} diff --git a/internal/metadata/meta.go b/internal/metadata/meta.go deleted file mode 100644 index 2a0bc25f62..0000000000 --- a/internal/metadata/meta.go +++ /dev/null @@ -1,181 +0,0 @@ -// Package metadata defines package summary facts and their LLPS binary format. -package metadata - -// Symbol is a module-level named entity participating in reachability. -type Symbol uint32 - -// Name is a plain name reference used for semantic matching. -type Name uint32 - -// MethodSig describes a method by short name and function type symbol. -type MethodSig struct { - Name Name - MType Symbol -} - -// MethodSlot describes one entry in a concrete type's ABI method table. -type MethodSlot struct { - Sig MethodSig - IFn Symbol - TFn Symbol -} - -// IfaceMethodDemand records one reachable interface method call. -type IfaceMethodDemand struct { - Target Symbol - Sig MethodSig -} - -// PackageMeta holds all single-package facts needed by whole-program analysis. -type PackageMeta struct { - stringTable []string - - ordinaryEdges map[Symbol][]Symbol - typeChildren map[Symbol][]Symbol - interfaceInfo map[Symbol][]MethodSig - useIface map[Symbol][]Symbol - useIfaceMethod map[Symbol][]IfaceMethodDemand - methodInfo map[Symbol][]MethodSlot - useNamedMethod map[Symbol][]Name - reflectMethod map[Symbol]struct{} -} - -// NewPackageMeta creates an empty PackageMeta with initialized maps. -func NewPackageMeta(stringTable []string) *PackageMeta { - table := append([]string(nil), stringTable...) - return &PackageMeta{ - stringTable: table, - ordinaryEdges: make(map[Symbol][]Symbol), - typeChildren: make(map[Symbol][]Symbol), - interfaceInfo: make(map[Symbol][]MethodSig), - useIface: make(map[Symbol][]Symbol), - useIfaceMethod: make(map[Symbol][]IfaceMethodDemand), - methodInfo: make(map[Symbol][]MethodSlot), - useNamedMethod: make(map[Symbol][]Name), - reflectMethod: make(map[Symbol]struct{}), - } -} - -// StringTable returns a copy of the package-local string table. -func (pm *PackageMeta) StringTable() []string { - if pm == nil { - return nil - } - return append([]string(nil), pm.stringTable...) -} - -// SymbolName returns the string referenced by a Symbol. -func (pm *PackageMeta) SymbolName(sym Symbol) string { - if pm == nil || int(sym) >= len(pm.stringTable) { - return "" - } - return pm.stringTable[sym] -} - -// Name returns the string referenced by a Name. -func (pm *PackageMeta) Name(ref Name) string { - if pm == nil || int(ref) >= len(pm.stringTable) { - return "" - } - return pm.stringTable[ref] -} - -// ForEachOrdinaryEdge visits each ordinary reachability edge group. -func (pm *PackageMeta) ForEachOrdinaryEdge(fn func(src Symbol, dsts []Symbol)) { - if pm == nil { - return - } - for src, dsts := range pm.ordinaryEdges { - fn(src, cloneSymbols(dsts)) - } -} - -// ForEachTypeChild visits each type-child edge group. -func (pm *PackageMeta) ForEachTypeChild(fn func(parent Symbol, children []Symbol)) { - if pm == nil { - return - } - for parent, children := range pm.typeChildren { - fn(parent, cloneSymbols(children)) - } -} - -// ForEachInterface visits each interface method set. -func (pm *PackageMeta) ForEachInterface(fn func(iface Symbol, methods []MethodSig)) { - if pm == nil { - return - } - for iface, methods := range pm.interfaceInfo { - fn(iface, cloneMethodSigs(methods)) - } -} - -// ForEachUseIface visits each function's concrete types used as interfaces. -func (pm *PackageMeta) ForEachUseIface(fn func(owner Symbol, types []Symbol)) { - if pm == nil { - return - } - for owner, types := range pm.useIface { - fn(owner, cloneSymbols(types)) - } -} - -// ForEachUseIfaceMethod visits each function's interface method demands. -func (pm *PackageMeta) ForEachUseIfaceMethod(fn func(owner Symbol, demands []IfaceMethodDemand)) { - if pm == nil { - return - } - for owner, demands := range pm.useIfaceMethod { - fn(owner, cloneIfaceMethodDemands(demands)) - } -} - -// ForEachMethodInfo visits each concrete type's method slots. -func (pm *PackageMeta) ForEachMethodInfo(fn func(typ Symbol, slots []MethodSlot)) { - if pm == nil { - return - } - for typ, slots := range pm.methodInfo { - fn(typ, cloneMethodSlots(slots)) - } -} - -// ForEachUseNamedMethod visits each function's constant MethodByName names. -func (pm *PackageMeta) ForEachUseNamedMethod(fn func(owner Symbol, names []Name)) { - if pm == nil { - return - } - for owner, names := range pm.useNamedMethod { - fn(owner, cloneNames(names)) - } -} - -// ForEachReflectMethod visits each function that needs conservative reflection handling. -func (pm *PackageMeta) ForEachReflectMethod(fn func(owner Symbol)) { - if pm == nil { - return - } - for owner := range pm.reflectMethod { - fn(owner) - } -} - -func cloneSymbols(in []Symbol) []Symbol { - return append([]Symbol(nil), in...) -} - -func cloneNames(in []Name) []Name { - return append([]Name(nil), in...) -} - -func cloneMethodSigs(in []MethodSig) []MethodSig { - return append([]MethodSig(nil), in...) -} - -func cloneIfaceMethodDemands(in []IfaceMethodDemand) []IfaceMethodDemand { - return append([]IfaceMethodDemand(nil), in...) -} - -func cloneMethodSlots(in []MethodSlot) []MethodSlot { - return append([]MethodSlot(nil), in...) -} diff --git a/internal/metadata/metadata_test.go b/internal/metadata/metadata_test.go deleted file mode 100644 index e23437c7f8..0000000000 --- a/internal/metadata/metadata_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package metadata - -import ( - "bytes" - "reflect" - "strings" - "testing" -) - -func TestBuilderSeparatesSymbolAndNameReferences(t *testing.T) { - b := NewBuilder() - - mainSym := b.Symbol("main") - mainName := b.Name("main") - readName := b.Name("Read") - fnType := b.Symbol("_llgo_func$A1") - typ := b.Symbol("*reader") - ifn := b.Symbol("(*reader).Read$iface") - tfn := b.Symbol("(*reader).Read") - - if uint32(mainSym) != uint32(mainName) { - t.Fatalf("shared string table should allow equal backing IDs for equal text: Symbol=%d Name=%d", mainSym, mainName) - } - - b.AddEdge(mainSym, tfn) - b.AddUseNamedMethod(mainSym, []Name{mainName, readName}) - b.AddMethodInfo(typ, []MethodSlot{{ - Sig: MethodSig{Name: readName, MType: fnType}, - IFn: ifn, - TFn: tfn, - }}) - - pm := b.Build() - if got := pm.SymbolName(mainSym); got != "main" { - t.Fatalf("SymbolName(main) = %q, want main", got) - } - if got := pm.Name(mainName); got != "main" { - t.Fatalf("Name(main) = %q, want main", got) - } - slots := collectMethodSlots(pm, typ) - if got := pm.Name(slots[0].Sig.Name); got != "Read" { - t.Fatalf("method name = %q, want Read", got) - } - if got := pm.SymbolName(slots[0].Sig.MType); got != "_llgo_func$A1" { - t.Fatalf("method type = %q, want _llgo_func$A1", got) - } -} - -func TestWriteReadMetaRoundTrip(t *testing.T) { - pm := buildFullTestMeta() - - var buf bytes.Buffer - if err := pm.WriteMeta(&buf); err != nil { - t.Fatalf("WriteMeta: %v", err) - } - - got, err := ReadMeta(bytes.NewReader(buf.Bytes())) - if err != nil { - t.Fatalf("ReadMeta: %v", err) - } - - if !reflect.DeepEqual(got, pm) { - t.Fatalf("round trip mismatch\ngot: %#v\nwant: %#v", got, pm) - } - - var buf2 bytes.Buffer - if err := got.WriteMeta(&buf2); err != nil { - t.Fatalf("WriteMeta after round trip: %v", err) - } - if !bytes.Equal(buf.Bytes(), buf2.Bytes()) { - t.Fatalf("WriteMeta should be deterministic across round trip") - } -} - -func TestReadMetaRejectsInvalidFiles(t *testing.T) { - if _, err := ReadMeta(strings.NewReader("NOPE")); err == nil { - t.Fatal("ReadMeta accepted bad magic") - } - - var buf bytes.Buffer - pm := buildFullTestMeta() - if err := pm.WriteMeta(&buf); err != nil { - t.Fatal(err) - } - data := append([]byte(nil), buf.Bytes()...) - data[4] = 99 - if _, err := ReadMeta(bytes.NewReader(data)); err == nil { - t.Fatal("ReadMeta accepted unsupported version") - } -} - -func TestFormatMetaStableOutput(t *testing.T) { - got := MetaString(buildFullTestMeta()) - want := `[TypeChildren] -*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: - _llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to -*_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner: - _llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner -*_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer: - _llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer -*_llgo_int: - _llgo_int -*_llgo_string: - _llgo_string -_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to: - _llgo_string -_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner: - _llgo_string -_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer: - _llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner - _llgo_int - -[InterfaceInfo] -_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo: - M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac - N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac - -[OrdinaryEdges] -github.com/goplus/llgo/cl/_testmeta/interface_anonymous.main: - github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use - -[UseIface] -github.com/goplus/llgo/cl/_testmeta/interface_anonymous.main: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T - -[UseIfaceMethod] -github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use: - _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac - _llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac - -[MethodInfo] -*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).M - 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).N github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).N -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T: - 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).M github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T.M - 1 N _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).N github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T.N - -[UseNamedMethod] -github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use: - M - -[ReflectMethod] -github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use - -` - if got != want { - t.Fatalf("MetaString mismatch\ngot:\n%s\nwant:\n%s", got, want) - } -} - -func buildFullTestMeta() *PackageMeta { - b := NewBuilder() - - nestedFuncPtr := b.Symbol("*_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to") - nestedFunc := b.Symbol("_llgo_func$zNDVRsWTIpUPKouNUS805RGX--IV9qVK8B31IZbg5to") - nestedInnerPtr := b.Symbol("*_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner") - nestedInner := b.Symbol("_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Inner") - nestedOuterPtr := b.Symbol("*_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer") - nestedOuter := b.Symbol("_llgo_github.com/goplus/llgo/cl/_testmeta/nested.Outer") - intPtr := b.Symbol("*_llgo_int") - intType := b.Symbol("_llgo_int") - stringPtr := b.Symbol("*_llgo_string") - stringType := b.Symbol("_llgo_string") - - b.AddTypeChild(nestedFuncPtr, nestedFunc) - b.AddTypeChild(nestedInnerPtr, nestedInner) - b.AddTypeChild(nestedOuterPtr, nestedOuter) - b.AddTypeChild(intPtr, intType) - b.AddTypeChild(stringPtr, stringType) - b.AddTypeChild(nestedFunc, stringType) - b.AddTypeChild(nestedInner, stringType) - b.AddTypeChild(nestedOuter, nestedInner) - b.AddTypeChild(nestedOuter, intType) - - main := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.main") - use := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.use") - iface := b.Symbol("_llgo_iface$f14WsslTA1u5wwC83jLU0HU2u2mmAWxBVE38vPBbRAo") - typ := b.Symbol("_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T") - ptrTyp := b.Symbol("*_llgo_github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T") - methodType := b.Symbol("_llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac") - mName := b.Name("M") - nName := b.Name("N") - mSig := MethodSig{Name: mName, MType: methodType} - nSig := MethodSig{Name: nName, MType: methodType} - ptrM := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).M") - ptrN := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.(*T).N") - valM := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T.M") - valN := b.Symbol("github.com/goplus/llgo/cl/_testmeta/interface_anonymous.T.N") - - b.AddEdge(main, use) - b.AddIfaceEntry(iface, []MethodSig{mSig, nSig}) - b.AddUseIface(main, []Symbol{typ}) - b.AddUseIfaceMethod(use, []IfaceMethodDemand{ - {Target: iface, Sig: mSig}, - {Target: iface, Sig: nSig}, - }) - b.AddMethodInfo(ptrTyp, []MethodSlot{ - {Sig: mSig, IFn: ptrM, TFn: ptrM}, - {Sig: nSig, IFn: ptrN, TFn: ptrN}, - }) - b.AddMethodInfo(typ, []MethodSlot{ - {Sig: mSig, IFn: ptrM, TFn: valM}, - {Sig: nSig, IFn: ptrN, TFn: valN}, - }) - b.AddUseNamedMethod(use, []Name{mName}) - b.AddReflectMethod(use) - - return b.Build() -} - -func collectMethodSlots(pm *PackageMeta, want Symbol) []MethodSlot { - var got []MethodSlot - pm.ForEachMethodInfo(func(typ Symbol, slots []MethodSlot) { - if typ == want { - got = append(got, slots...) - } - }) - return got -} From 8653a64ebf04d62247ed3426eb0163c723f4417f Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sat, 4 Jul 2026 14:19:35 +0800 Subject: [PATCH 34/37] test: drop interface hash patch path experiment --- ssa/abi/abi.go | 2 +- ssa/abi/abi_coverage_test.go | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/ssa/abi/abi.go b/ssa/abi/abi.go index 138b364cfa..78e00d18f1 100644 --- a/ssa/abi/abi.go +++ b/ssa/abi/abi.go @@ -349,7 +349,7 @@ func (b *Builder) interfaceHash(t *types.Interface) (ret []byte, pkg string) { for i := 0; i < n; i++ { m := t.Method(i) if !m.Exported() && pkg == "" { - pkg = PathOf(m.Pkg()) + pkg = m.Pkg().Path() } ft := b.FuncName(m.Type().(*types.Signature)) fmt.Fprintln(h, m.Name(), ft) diff --git a/ssa/abi/abi_coverage_test.go b/ssa/abi/abi_coverage_test.go index 8b28d91497..462a61624d 100644 --- a/ssa/abi/abi_coverage_test.go +++ b/ssa/abi/abi_coverage_test.go @@ -286,22 +286,6 @@ func TestStructInterfaceClosureAndPathCoverage(t *testing.T) { t.Fatalf("InterfaceName(private iface)=(%q,%v), want %q*,false", got, pub, pkg.Path()+".iface$") } - realReflectlite := types.NewPackage("internal/reflectlite", "reflectlite") - patchReflectlite := types.NewPackage(PatchPathPrefix+"internal/reflectlite", "reflectlite") - realIface := types.NewInterfaceType([]*types.Func{ - types.NewFunc(token.NoPos, realReflectlite, "common", sig), - }, nil) - realIface.Complete() - patchIface := types.NewInterfaceType([]*types.Func{ - types.NewFunc(token.NoPos, patchReflectlite, "common", sig), - }, nil) - patchIface.Complete() - realName, _ := b.InterfaceName(realIface) - patchName, _ := b.InterfaceName(patchIface) - if realName != patchName { - t.Fatalf("InterfaceName should canonicalize patch package path: real=%q patch=%q", realName, patchName) - } - if got := PathOf(nil); got != "" { t.Fatalf("PathOf(nil)=%q, want empty", got) } From 3b029f8c0582363594f9eff7b931814db7c3a693 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sat, 4 Jul 2026 16:07:55 +0800 Subject: [PATCH 35/37] meta: record interface method demands structurally --- cl/_testmeta/interface_exported_var/meta-expect.txt | 4 ++-- cl/_testmeta/interface_imported/meta-expect.txt | 4 ++-- cl/_testmeta/interface_named/meta-expect.txt | 4 ++-- cl/_testmeta/interface_unexported/meta-expect.txt | 4 ++-- cl/_testmeta/methodinfo_imported/meta-expect.txt | 4 ++-- ssa/interface.go | 11 +---------- ssa/ssa_test.go | 11 +++++++---- 7 files changed, 18 insertions(+), 24 deletions(-) diff --git a/cl/_testmeta/interface_exported_var/meta-expect.txt b/cl/_testmeta/interface_exported_var/meta-expect.txt index 457e68d525..4be1e14c08 100644 --- a/cl/_testmeta/interface_exported_var/meta-expect.txt +++ b/cl/_testmeta/interface_exported_var/meta-expect.txt @@ -316,7 +316,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_exported_var.main: - _llgo_encoding/binary.ByteOrder Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus + _llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw Uint16 _llgo_func$hV5ojfmZDe6MffI0ylemgDF5GZlZiYaJbQvka2A0Gus [MethodInfo] *_llgo_encoding/binary.littleEndian: @@ -345,7 +345,7 @@ _llgo_encoding/binary.littleEndian: 10 Uint64 _llgo_func$mjkdaEUHPtpOYlUrWGfnskhhvdyc7k9Fk-vwWj3VftE encoding/binary.(*littleEndian).Uint64 __llgo_stub.encoding/binary.littleEndian.Uint64 [InterfaceInfo] -_llgo_encoding/binary.ByteOrder: +_llgo_iface$J1wM-rGcIPemx5jloXBmH7pUzUCSqpgNkOdb0QIFTxw: PutUint16 _llgo_func$ibbVVENlZHDgol7ROnCbrRmXPuO818NXcpl5dfFoKhU PutUint32 _llgo_func$YjgNCugJxKXYLk39KOJyRyLtvcHU1d7KRz4inhHdVgg PutUint64 _llgo_func$mBhSCdZCFK2IHVQVA73dFmon0gMcig2Q387khsbzmm8 diff --git a/cl/_testmeta/interface_imported/meta-expect.txt b/cl/_testmeta/interface_imported/meta-expect.txt index 19b8845300..91ec9613ac 100644 --- a/cl/_testmeta/interface_imported/meta-expect.txt +++ b/cl/_testmeta/interface_imported/meta-expect.txt @@ -149,7 +149,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_imported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_imported.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Reader Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: @@ -160,6 +160,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source: 1 Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk github.com/goplus/llgo/cl/_testmeta/interface_imported/api.(*Source).Read __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Source.Read [InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_imported/api.Reader: +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk diff --git a/cl/_testmeta/interface_named/meta-expect.txt b/cl/_testmeta/interface_named/meta-expect.txt index c9133a82ab..e13b51bb7c 100644 --- a/cl/_testmeta/interface_named/meta-expect.txt +++ b/cl/_testmeta/interface_named/meta-expect.txt @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_named.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + _llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: @@ -69,6 +69,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.T: 0 M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_named.(*T).M __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_named.T.M [InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_named.I: +_llgo_iface$anEWstLioBmxcO9rxTXClbAzDIEQLw2tApwq0mcSt88: M _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/interface_unexported/meta-expect.txt b/cl/_testmeta/interface_unexported/meta-expect.txt index 0e8d32cc9c..26ce33099c 100644 --- a/cl/_testmeta/interface_unexported/meta-expect.txt +++ b/cl/_testmeta/interface_unexported/meta-expect.txt @@ -60,7 +60,7 @@ github.com/goplus/llgo/cl/_testmeta/interface_unexported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/interface_unexported.use: - _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac + github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac [MethodInfo] *_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: @@ -69,6 +69,6 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.T: 0 github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/interface_unexported.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/interface_unexported.T.m [InterfaceInfo] -_llgo_github.com/goplus/llgo/cl/_testmeta/interface_unexported.I: +github.com/goplus/llgo/cl/_testmeta/interface_unexported.iface$SE5y-KS93u1u9p1qAf9LRB1hncS44rJIq27_JZbIQVo: github.com/goplus/llgo/cl/_testmeta/interface_unexported.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac diff --git a/cl/_testmeta/methodinfo_imported/meta-expect.txt b/cl/_testmeta/methodinfo_imported/meta-expect.txt index db25d4a6df..49a0fe2c86 100644 --- a/cl/_testmeta/methodinfo_imported/meta-expect.txt +++ b/cl/_testmeta/methodinfo_imported/meta-expect.txt @@ -517,7 +517,7 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: - _llgo_io.Reader Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk + _llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk [MethodInfo] *_llgo_bytes.Buffer: @@ -550,6 +550,6 @@ github.com/goplus/llgo/cl/_testmeta/methodinfo_imported.main: 26 bytes.tryGrowByReslice _llgo_func$qVJ5SH6qhXP_h0AM41vpBGzQEMp-fQIfvwQEJy5NI8M bytes.(*Buffer).tryGrowByReslice __llgo_stub.bytes.(*Buffer).tryGrowByReslice [InterfaceInfo] -_llgo_io.Reader: +_llgo_iface$uycIKA3bbxRhudEjW1hHKWKdLqHQsCVy8NdW1bkQmNw: Read _llgo_func$G2hch9Iy9DrhKKsg70PbL54bK-XSl-1IUUORN17J2Dk diff --git a/ssa/interface.go b/ssa/interface.go index 3a79f118aa..0a9cd2c287 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -83,16 +83,7 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr { tclosure := prog.Type(sig, InGo) i := iMethodOf(rawIntf, method.Name()) if mb := b.Pkg.MetaBuilder; mb != nil { - intfSymType := intfType - if _, ok := intfSymType.(*types.TypeParam); ok { - intfSymType = rawIntf - } - if named, ok := types.Unalias(intfSymType).(*types.Named); ok { - if targs := named.TypeArgs(); targs != nil && targs.Len() > 0 { - intfSymType = rawIntf - } - } - intfSymName := func() string { n, _ := prog.abi.TypeName(intfSymType); return n }() + intfSymName := func() string { n, _ := prog.abi.TypeName(rawIntf); return n }() intfSym := mb.Sym(intfSymName) b.Pkg.recordInterfaceInfo(rawIntf, intfSymName) // Record which interface method is demanded. i is the method's index in rawIntf. diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index dce9a1fc44..317aef651f 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -1207,11 +1207,14 @@ func TestIfaceMethodClosureCallIR(t *testing.T) { } defer pm.Close() gotMeta := pm.String() - if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt Printf ") { - t.Fatalf("UseIfaceMethod should target named interface symbol and resolve its signature, got:\n%s", gotMeta) + if strings.Contains(gotMeta, "_llgo_foo/bar.IFmt Printf ") { + t.Fatalf("UseIfaceMethod should not target named interface symbol, got:\n%s", gotMeta) } - if !strings.Contains(gotMeta, "_llgo_foo/bar.IFmt:\n Printf ") { - t.Fatalf("UseIfaceMethod callsite should record InterfaceInfo for the same symbol, got:\n%s", gotMeta) + if !strings.Contains(gotMeta, "iface$") || !strings.Contains(gotMeta, " Printf ") { + t.Fatalf("UseIfaceMethod should target structural interface symbol and resolve its signature, got:\n%s", gotMeta) + } + if !strings.Contains(gotMeta, "iface$") || !strings.Contains(gotMeta, ":\n Printf ") { + t.Fatalf("UseIfaceMethod callsite should record InterfaceInfo for the same structural symbol, got:\n%s", gotMeta) } assertPkg(t, pkg, `; ModuleID = 'foo/bar' From 9b1783d9068b0da73bcadeb2cf15fb2a3cab92fb Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sat, 4 Jul 2026 20:14:18 +0800 Subject: [PATCH 36/37] meta: canonicalize patched interface demands --- internal/build/build.go | 30 +++++++++++++++++++++++++++++- ssa/interface.go | 3 ++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index 6b8bae0caa..12e999cd69 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -24,6 +24,7 @@ import ( "go/constant" "go/token" "go/types" + "io" "log" "os" "os/exec" @@ -1147,11 +1148,38 @@ func applyDCEOverrides(ctx *context, mainPkg *packages.Package, pkgs []Package, } fmt.Fprintf(os.Stderr, "[dce] packages=%d roots=%s merge=%v analyze=%v live method slots=%d types=%d\n", len(metas), strings.Join(roots, ","), mergeDur, analyzeDur, liveCount, len(liveSlots)) - return dcepass.EmitStrongTypeOverridesDebug(entryPkg.LPkg.Module(), dceSourceModules(pkgs), liveSlots, os.Stderr) + err := dcepass.EmitStrongTypeOverridesDebug(entryPkg.LPkg.Module(), dceSourceModules(pkgs), liveSlots, os.Stderr) + printDCEMetaInputs(ctx, pkgs, os.Stderr) + return err } return dcepass.EmitStrongTypeOverrides(entryPkg.LPkg.Module(), dceSourceModules(pkgs), liveSlots) } +func printDCEMetaInputs(ctx *context, pkgs []Package, w io.Writer) { + cm := ctx.ensureCacheManager() + targetTriple := ctx.targetTriple() + fmt.Fprintf(w, "[dce] meta inputs:\n") + for _, pkg := range pkgs { + if pkg == nil || pkg.Meta == nil { + continue + } + if pkg.Fingerprint == "" || pkg.Name == "main" { + fmt.Fprintf(w, "[dce] %s memory\n", pkg.PkgPath) + continue + } + paths := cm.PackagePaths(targetTriple, pkg.PkgPath, pkg.Fingerprint) + state := "miss" + if pkg.CacheHit { + state = "hit" + } + exists := "missing" + if _, err := os.Stat(paths.Meta); err == nil { + exists = "exists" + } + fmt.Fprintf(w, "[dce] %s %s %s %s\n", pkg.PkgPath, state, exists, paths.Meta) + } +} + func linkedPackageMetas(pkgs []Package) []*meta.PackageMeta { metas := make([]*meta.PackageMeta, 0, len(pkgs)) for _, pkg := range pkgs { diff --git a/ssa/interface.go b/ssa/interface.go index 0a9cd2c287..14d5714cff 100644 --- a/ssa/interface.go +++ b/ssa/interface.go @@ -67,7 +67,8 @@ func iMethodOf(rawIntf *types.Interface, name string) int { func (b Builder) Imethod(intf Expr, method *types.Func) Expr { prog := b.Prog intfType := types.Unalias(intf.raw.Type) - rawIntf := intfType.Underlying().(*types.Interface) + patchedIntfType := prog.patch(intfType) + rawIntf := patchedIntfType.Underlying().(*types.Interface) sig := method.Type().(*types.Signature) if sig.Recv() == nil && sig.Params().Len() > 0 { pt := types.Unalias(sig.Params().At(0).Type()) From 8d9d0925f327c1448fc565c47fe7d6d22dc0fe3f Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sat, 4 Jul 2026 20:20:04 +0800 Subject: [PATCH 37/37] test: update reflect meta golden --- cl/_testmeta/reflect_named/meta-expect.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cl/_testmeta/reflect_named/meta-expect.txt b/cl/_testmeta/reflect_named/meta-expect.txt index 187a7c38b4..9633334568 100644 --- a/cl/_testmeta/reflect_named/meta-expect.txt +++ b/cl/_testmeta/reflect_named/meta-expect.txt @@ -52,8 +52,8 @@ github.com/goplus/llgo/cl/_testmeta/reflect_named.main: [UseIfaceMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: - _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E - _llgo_reflect.Type MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E + github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI MethodByName _llgo_func$aM2cVUtLQbPq1YHtnabQiM7XJ5Cg5RyV6BIDWrqey7E [UseNamedMethod] github.com/goplus/llgo/cl/_testmeta/reflect_named.main: @@ -69,7 +69,7 @@ _llgo_github.com/goplus/llgo/cl/_testmeta/reflect_named.T: 1 github.com/goplus/llgo/cl/_testmeta/reflect_named.m _llgo_func$2_iS07vIlF2_rZqWB5eU0IvP_9HviM4MYZNkXZDvbac github.com/goplus/llgo/cl/_testmeta/reflect_named.(*T).m __llgo_stub.github.com/goplus/llgo/cl/_testmeta/reflect_named.T.m [InterfaceInfo] -_llgo_reflect.Type: +github.com/goplus/llgo/runtime/internal/lib/reflect.iface$iKaf3qt2OaMUwQszh7IENnt26Bv3ufKgLDSVmXKN7gI: Align _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA AssignableTo _llgo_func$Kxk9fspGkjXcoNWf2ucHG1vOQ5VHxVtYionfm-DnvWE Bits _llgo_func$ETeB8WwW04JEq0ztcm-XPTJtuYvtpkjIsAc0-2NT9zA