From 937bf082a066bdf13379ccb6d8100ccdb0ffaac6 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 7 Feb 2026 20:10:30 +0800 Subject: [PATCH 1/5] ssa: prune abitypes by callgraph --- cl/instr.go | 1 + internal/build/main_module.go | 43 ++++++++++++++ ssa/abitype.go | 106 ++++++++++++++++++++++++++++------ ssa/package.go | 42 ++++++++++++-- ssa/ssa_test.go | 76 ++++++++++++++++++++++++ 5 files changed, 247 insertions(+), 21 deletions(-) diff --git a/cl/instr.go b/cl/instr.go index c772c67de7..35c211e9bd 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -678,6 +678,7 @@ func (p *context) pkgNoInit(pkg *types.Package) bool { func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon) (ret llssa.Expr) { cv := call.Value if mthd := call.Method; mthd != nil { + p.prog.AddInvoke(mthd) o := p.compileValue(b, cv) fn := b.Imethod(o, mthd) hasVArg := fnNormal diff --git a/internal/build/main_module.go b/internal/build/main_module.go index 31c545af72..6eac2b0c20 100644 --- a/internal/build/main_module.go +++ b/internal/build/main_module.go @@ -32,6 +32,9 @@ import ( "github.com/goplus/llgo/internal/packages" llvm "github.com/goplus/llvm" + "golang.org/x/tools/go/callgraph" + "golang.org/x/tools/go/callgraph/cha" + "golang.org/x/tools/go/ssa" llssa "github.com/goplus/llgo/ssa" ) @@ -82,6 +85,32 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, needRu rtInit = declareNoArgFunc(mainPkg, rtPkgPath+".init") } + if !IsDbgEnabled() && !needAbiInit { + progSSA := ctx.progSSA + chaGraph := cha.CallGraph(progSSA) + //vtaGraph := vta.CallGraph(ssautil.AllFunctions(progSSA), chaGraph) + //_ = vtaGraph + invoked := buildInvokeIndex(chaGraph) + mainPkg.PruneAbiTypes(func(sel *types.Selection) bool { + method := progSSA.MethodValue(sel) + if _, ok := invoked[method]; ok { + return true + } + for v := range invoked { + if v.Name() == method.Name() { + if !types.Identical(prog.PatchType(v.Type().(*types.Signature).Recv().Type()), sel.Type().(*types.Signature).Recv().Type()) { + continue + } + if !types.Identical(prog.PatchType(v.Type()), sel.Type()) { + continue + } + return true + } + } + return false + }) + } + var abiInit llssa.Function if needAbiInit { abiInit = mainPkg.InitAbiTypes("init$abitypes") @@ -99,6 +128,20 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, needRu return mainAPkg } +func buildInvokeIndex(cg *callgraph.Graph) map[*ssa.Function]bool { + invoked := make(map[*ssa.Function]bool) + for _, node := range cg.Nodes { + for _, out := range node.Out { + if out.Callee != nil && out.Callee.Func != nil { + if out.Site != nil && out.Site.Common().IsInvoke() { + invoked[out.Callee.Func] = true + } + } + } + } + return invoked +} + // defineEntryFunction creates the program's entry function. The name is // "main" for standard targets, or "__main_argc_argv" with hidden visibility // for WASM targets that don't require _start. diff --git a/ssa/abitype.go b/ssa/abitype.go index 396e0a6f72..9e40f4d4a0 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -347,14 +347,14 @@ func (b Builder) abiUncommonMethodSet(t types.Type) (mset *types.MethodSet, ok b } mset := types.NewMethodSet(t) if mset.Len() != 0 { - if prog.compileMethods != nil { + if prog.compileMethods != nil && !prog.abiTypePruning { prog.compileMethods(b.Pkg, t) } } return mset, true case *types.Struct, *types.Pointer: if mset := types.NewMethodSet(t); mset.Len() != 0 { - if prog.compileMethods != nil { + if prog.compileMethods != nil && !prog.abiTypePruning { prog.compileMethods(b.Pkg, t) } return mset, true @@ -401,7 +401,7 @@ type Method struct { } */ -func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Value { +func (b Builder) abiUncommonMethods(t types.Type, name string, mset *types.MethodSet) llvm.Value { prog := b.Prog ft := prog.rtType("Method") n := mset.Len() @@ -409,7 +409,13 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va pkg, _ := b.abiUncommonPkg(t) anonymous := pkg == nil if anonymous { - pkg = types.NewPackage(b.Pkg.Path(), "") + pkgPath := b.Pkg.Path() + if prog.abiTypePruning { + if sym, ok := prog.abiSymbol[name]; ok { + pkgPath = sym.pkgPath + } + } + pkg = types.NewPackage(pkgPath, "") } for i := 0; i < n; i++ { m := mset.At(i) @@ -421,12 +427,17 @@ func (b Builder) abiUncommonMethods(t types.Type, mset *types.MethodSet) llvm.Va } mSig := m.Type().(*types.Signature) var tfn, ifn llvm.Value - tfn = b.abiMethodFunc(anonymous, pkg, mName, mSig) - ifn = tfn - if _, ok := m.Recv().Underlying().(*types.Pointer); !ok { - pRecv := types.NewVar(token.NoPos, pkg, "", types.NewPointer(mSig.Recv().Type())) - pSig := types.NewSignature(pRecv, mSig.Params(), mSig.Results(), mSig.Variadic()) - ifn = b.abiMethodFunc(anonymous, pkg, mName, pSig) + if prog.abiTypePruning && !prog.methodIsInvoke(m) { + tfn = prog.Nil(prog.VoidPtr()).impl + ifn = tfn + } else { + tfn = b.abiMethodFunc(anonymous, pkg, mName, mSig) + ifn = tfn + if _, ok := m.Recv().Underlying().(*types.Pointer); !ok { + pRecv := types.NewVar(token.NoPos, pkg, "", types.NewPointer(mSig.Recv().Type())) + pSig := types.NewSignature(pRecv, mSig.Params(), mSig.Results(), mSig.Variadic()) + ifn = b.abiMethodFunc(anonymous, pkg, mName, pSig) + } } var values []llvm.Value values = append(values, name) @@ -448,7 +459,7 @@ func funcType(prog Program, typ types.Type) types.Type { func (b Builder) abiMethodFunc(anonymous bool, mPkg *types.Package, mName string, mSig *types.Signature) (tfn llvm.Value) { var fullName string if anonymous { - fullName = b.Pkg.Path() + "." + mSig.Recv().Type().String() + "." + mName + fullName = mPkg.Path() + "." + mSig.Recv().Type().String() + "." + mName } else { fullName = FuncName(mPkg, mName, mSig.Recv(), false) } @@ -471,14 +482,31 @@ func (b Builder) abiMethodFunc(anonymous bool, mPkg *types.Package, mName string } */ func (b Builder) abiType(t types.Type) Expr { - name, _ := b.Pkg.abi.TypeName(t) - g := b.Pkg.VarOf(name) prog := b.Prog + var name string + if v, ok := prog.abiTypeName[t]; ok { + name = v + } else { + name, _ = b.Pkg.abi.TypeName(t) + } + g := b.Pkg.VarOf(name) pkg := b.Pkg if g == nil { + raw := t if prog.patchType != nil { t = prog.patchType(t) } + prog.abiTypeName[raw] = name + if prog.abiTypePruning { + if sym, ok := prog.abiSymbol[name]; ok { + pkgPath := pkg.abi.Pkg + pkg.abi.Pkg = sym.pkgPath + defer func() { + pkg.abi.Pkg = pkgPath + }() + } + } + mset, hasUncommon := b.abiUncommonMethodSet(t) rt := prog.rtNamed(pkg.abi.RuntimeName(t)) var typ types.Type = rt @@ -503,13 +531,15 @@ func (b Builder) abiType(t types.Type) Expr { fields = []llvm.Value{ llvm.ConstNamedStruct(prog.Type(rt, InGo).ll, fields), b.abiUncommonType(t, mset), - b.abiUncommonMethods(t, mset), + b.abiUncommonMethods(t, name, mset), } } g.impl.SetInitializer(prog.ctx.ConstStruct(fields, false)) g.impl.SetGlobalConstant(true) - g.impl.SetLinkage(llvm.WeakODRLinkage) - prog.abiSymbol[name] = g.Type + if !prog.abiTypePruning { + g.impl.SetLinkage(llvm.WeakODRLinkage) + prog.abiSymbol[name] = &AbiSymbol{raw: raw, typ: g.Type, pkgPath: pkg.Path(), uncommon: hasUncommon} + } } return Expr{llvm.ConstGEP(g.impl.GlobalValueType(), g.impl, []llvm.Value{ llvm.ConstInt(prog.Int32().ll, 0, false), @@ -517,6 +547,13 @@ func (b Builder) abiType(t types.Type) Expr { }), prog.AbiTypePtr()} } +type AbiSymbol struct { + raw types.Type + typ Type + pkgPath string + uncommon bool +} + func (p Package) getAbiTypes(name string) Expr { prog := p.Prog names := make([]string, len(prog.abiSymbol)) @@ -528,7 +565,7 @@ func (p Package) getAbiTypes(name string) Expr { sort.Strings(names) fields := make([]llvm.Value, len(names)) for i, name := range names { - g := p.doNewVar(name, prog.abiSymbol[name]) + g := p.doNewVar(name, prog.abiSymbol[name].typ) g.impl.SetLinkage(llvm.ExternalLinkage) g.impl.SetGlobalConstant(true) ptr := Expr{llvm.ConstGEP(g.impl.GlobalValueType(), g.impl, []llvm.Value{ @@ -568,4 +605,39 @@ func (p Package) InitAbiTypes(fname string) Function { return initFn } +func (p Package) PruneAbiTypes(methodIsInvoke func(method *types.Selection) bool) { + prog := p.Prog + var names []string + for k, sym := range prog.abiSymbol { + if !sym.uncommon { + continue + } + names = append(names, k) + } + sort.Strings(names) + p.SetResolveLinkname(prog.resolveLinkname) + if methodIsInvoke == nil { + methodIsInvoke = func(method *types.Selection) bool { + if ms, ok := prog.invokeMethods[method.Obj().Name()]; ok { + for m := range ms { + if types.Identical(m, method.Type()) { + return true + } + } + } + return false + } + } + prog.abiTypePruning = true + defer func() { + prog.abiTypePruning = false + }() + prog.methodIsInvoke = methodIsInvoke + b := (&aFunction{Pkg: p, Prog: prog}).NewBuilder() + for _, name := range names { + sym := prog.abiSymbol[name] + b.abiType(sym.raw) + } +} + // ----------------------------------------------------------------------------- diff --git a/ssa/package.go b/ssa/package.go index f0ebd564b8..be45174593 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -22,6 +22,7 @@ import ( "go/types" "runtime" "strconv" + "strings" "unsafe" "github.com/goplus/llgo/internal/env" @@ -210,15 +211,32 @@ type aProgram struct { printfTy *types.Signature - paramObjPtr_ *types.Var - linkname map[string]string // pkgPath.nameInPkg => linkname - abiSymbol map[string]Type // abi symbol name => Type + paramObjPtr_ *types.Var + linkname map[string]string // pkgPath.nameInPkg => linkname + abiSymbol map[string]*AbiSymbol // abi symbol name => Type + abiTypeName map[types.Type]string + abiTypePruning bool + methodIsInvoke func(method *types.Selection) bool + + invokeMethods map[string]map[types.Type]none ptrSize int is32Bits bool } +type none struct{} + +func (p Program) AddInvoke(fn *types.Func) { + name := fn.Name() + m, ok := p.invokeMethods[name] + if !ok { + m = make(map[types.Type]none) + p.invokeMethods[name] = m + } + m[p.patch(fn.Type())] = none{} +} + // A Program presents a program. type Program = *aProgram @@ -263,7 +281,8 @@ func NewProgram(target *Target) Program { ctx: ctx, gocvt: newGoTypes(), target: target, td: td, tm: tm, is32Bits: is32Bits, ptrSize: td.PointerSize(), named: make(map[string]Type), fnnamed: make(map[string]int), - linkname: make(map[string]string), abiSymbol: make(map[string]Type), + linkname: make(map[string]string), abiSymbol: make(map[string]*AbiSymbol), + abiTypeName: make(map[types.Type]string), invokeMethods: make(map[string]map[types.Type]none), } } @@ -287,6 +306,10 @@ func (p Program) SetPatch(patchType func(types.Type) types.Type) { p.patchType = patchType } +func (p Program) PatchType(typ types.Type) types.Type { + return p.patch(typ) +} + func (p Program) patch(typ types.Type) types.Type { if p.patchType != nil { return p.patchType(typ) @@ -322,6 +345,17 @@ func (p Program) Linkname(name string) (link string, ok bool) { return } +func (p Program) resolveLinkname(name string) string { + if link, ok := p.linkname[name]; ok { + prefix, ltarget, _ := strings.Cut(link, ".") + if prefix != "C" { + panic("resolveLinkname: invalid link: " + link) + } + return ltarget + } + return name +} + func (p Program) runtime() *types.Package { if p.rt == nil { p.rt = p.rtget() diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 913e082898..d04a7c04e3 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -1261,6 +1261,82 @@ func TestTargetMachineAndDataLayout(t *testing.T) { } } +func TestAbiPrune(t *testing.T) { + prog := NewProgram(nil) + prog.sizes = types.SizesFor("gc", runtime.GOARCH) + prog.SetRuntime(func() *types.Package { + pkg, err := importer.For("source", nil).Import(PkgRuntime) + if err != nil { + t.Fatal(err) + } + return pkg + }) + pkg := prog.NewPackage("bar", "foo/bar") + + emptyIface := types.NewInterfaceType(nil, nil) + emptyIface.Complete() + emptyType := prog.Type(emptyIface, InGo) + + makeFn := func(name string, x Expr) { + sig := types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(types.NewVar(0, nil, "", emptyIface)), false) + fn := pkg.NewFunc(name, sig, InGo) + b := fn.MakeBody(1) + iface := b.MakeInterface(emptyType, x) + b.Return(iface) + } + + makeFn("intIface", prog.Val(1)) + makeFn("ptrIface", prog.Nil(prog.VoidPtr())) + makeFn("floatIface", prog.FloatVal(3.5, prog.Float32())) + + st := types.NewStruct([]*types.Var{ + types.NewVar(0, nil, "a", types.Typ[types.Int]), + types.NewVar(0, nil, "b", types.Typ[types.Int]), + }, nil) + makeFn("structIface", prog.Zero(prog.Type(st, InGo))) + + single := types.NewStruct([]*types.Var{ + types.NewVar(0, nil, "v", types.Typ[types.Int]), + }, nil) + makeFn("singleFieldIface", prog.Zero(prog.Type(single, InGo))) + + pkgTypes := types.NewPackage("foo/bar", "bar") + obj := types.NewTypeName(token.NoPos, pkgTypes, "Point", nil) + named := types.NewNamed(obj, types.Typ[types.Int], nil) + msig := types.NewSignatureType(types.NewVar(0, pkgTypes, "", named), nil, nil, nil, nil, false) + mthd := types.NewFunc(0, pkgTypes, "M", msig) + mthd2 := types.NewFunc(0, pkgTypes, "m2", msig) + named.AddMethod(mthd) + named.AddMethod(mthd2) + prog.AddInvoke(mthd) + pkgTypes.Scope().Insert(obj) + + anonStruct := types.NewStruct( + []*types.Var{ + types.NewField(0, pkgTypes, "", named, true), + types.NewField(0, pkgTypes, "Age", types.Typ[types.Int], false), + types.NewField(0, pkgTypes, "Tags", types.NewSlice(types.Typ[types.String]), false), + }, nil, + ) + rawSig := types.NewSignatureType(nil, nil, nil, nil, nil, false) + rawMeth := types.NewFunc(0, pkgTypes, "M", rawSig) + nonEmpty := types.NewInterfaceType([]*types.Func{rawMeth}, nil) + nonEmpty.Complete() + nonEmptyType := prog.Type(nonEmpty, InGo) + sigNE := types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(types.NewVar(0, nil, "", nonEmpty)), false) + fnNE := pkg.NewFunc("nonEmptyIface", sigNE, InGo) + bNE := fnNE.MakeBody(1) + bNE.MakeInterface(nonEmptyType, prog.Zero(prog.Type(anonStruct, InGo))) + bNE.Return(bNE.MakeInterface(nonEmptyType, prog.Zero(prog.Type(named, InGo)))) + + mainpkg := prog.NewPackage("main", "") + mainpkg.PruneAbiTypes(nil) + s := mainpkg.String() + if !strings.Contains(s, `@"*_llgo_foo/bar.Point" = constant { %"github.com/goplus/llgo/runtime/abi.PtrType",`) { + t.Fatal("error puretype", s) + } +} + func TestAbiTables(t *testing.T) { prog := NewProgram(nil) prog.sizes = types.SizesFor("gc", runtime.GOARCH) From 7d1d562b88fcdac0aa40eb391a1bcb2ba3d980cc Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 11 Feb 2026 18:20:11 +0800 Subject: [PATCH 2/5] ssa: pruneAbiTypes check needAbiInit --- cl/builtin_test.go | 63 --------------------------- cl/compile.go | 13 +----- internal/build/build.go | 7 ++- internal/build/main_module.go | 36 ++++++++++------ internal/build/main_module_test.go | 4 +- ssa/abitype.go | 23 ++++++---- ssa/package.go | 12 ++---- ssa/ssa_test.go | 68 +++++++++++++++++++++++++++++- ssa/type.go | 4 +- 9 files changed, 119 insertions(+), 111 deletions(-) diff --git a/cl/builtin_test.go b/cl/builtin_test.go index d2338e966b..e954be9558 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -447,69 +447,6 @@ func TestErrVarOf(t *testing.T) { ctx.varOf(nil, g) } -func TestContextResolveLinkname(t *testing.T) { - tests := []struct { - name string - link map[string]string - input string - want string - panics bool - }{ - { - name: "Normal", - link: map[string]string{ - "foo": "C.bar", - }, - input: "foo", - want: "bar", - }, - { - name: "MultipleLinks", - link: map[string]string{ - "foo1": "C.bar1", - "foo2": "C.bar2", - }, - input: "foo2", - want: "bar2", - }, - { - name: "NoLink", - link: map[string]string{}, - input: "foo", - want: "foo", - }, - { - name: "InvalidLink", - link: map[string]string{ - "foo": "invalid.bar", - }, - input: "foo", - panics: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.panics { - defer func() { - if r := recover(); r == nil { - t.Error("want panic") - } - }() - } - ctx := &context{prog: llssa.NewProgram(nil)} - for k, v := range tt.link { - ctx.prog.SetLinkname(k, v) - } - got := ctx.resolveLinkname(tt.input) - if !tt.panics { - if got != tt.want { - t.Errorf("got %q, want %q", got, tt.want) - } - } - }) - } -} - func TestInstantiate(t *testing.T) { obj := types.NewTypeName(0, nil, "T", nil) named := types.NewNamed(obj, types.Typ[types.Int], nil) diff --git a/cl/compile.go b/cl/compile.go index c40780969c..07fdd81389 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -1218,7 +1218,7 @@ func newPackageEx(prog llssa.Program, patches Patches, rewrites map[string]strin ctx.initFiles(pkgPath, files, pkgName == "C") ctx.prog.SetPatch(ctx.patchType) ctx.prog.SetCompileMethods(ctx.checkCompileMethods) - ret.SetResolveLinkname(ctx.resolveLinkname) + ret.SetResolveLinkname(ctx.prog.ResolveLinkname) if hasPatch { skips := ctx.skips @@ -1349,17 +1349,6 @@ func instantiate(orig types.Type, t *types.Named) (typ types.Type) { return } -func (p *context) resolveLinkname(name string) string { - if link, ok := p.prog.Linkname(name); ok { - prefix, ltarget, _ := strings.Cut(link, ".") - if prefix != "C" { - panic("resolveLinkname: invalid link: " + link) - } - return ltarget - } - return name -} - // checkCompileMethods ensures that all methods attached to the given type // (and to the types it refers to) are compiled and emitted into the // current SSA package. Generic named types and struct types are the diff --git a/internal/build/build.go b/internal/build/build.go index 841a6dbf03..0409c9bb41 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -946,7 +946,12 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa // Generate main module file (needed for global variables even in library modes) // This is compiled directly to .o and added to linkInputs (not cached) // Use a stable synthetic name to avoid confusing it with the real main package in traces/logs. - entryPkg := genMainModule(ctx, llssa.PkgRuntime, pkg, needRuntime, needPyInit, needAbiInit) + entryPkg := genMainModule(ctx, llssa.PkgRuntime, pkg, &genConfig{ + rtInit: needRuntime, + pyInit: needPyInit, + abiInit: needAbiInit, + abiPrune: !IsDbgEnabled(), + }) entryObjFile, err := exportObject(ctx, "entry_main", entryPkg.ExportFile, []byte(entryPkg.LPkg.String())) if err != nil { return err diff --git a/internal/build/main_module.go b/internal/build/main_module.go index 6eac2b0c20..8d10330a1a 100644 --- a/internal/build/main_module.go +++ b/internal/build/main_module.go @@ -27,6 +27,7 @@ package build import ( + "go/ast" "go/token" "go/types" @@ -39,12 +40,20 @@ import ( llssa "github.com/goplus/llgo/ssa" ) +// genConfig controls the code generation behavior for the main module. +type genConfig struct { + rtInit bool + pyInit bool + abiInit bool + abiPrune bool +} + // genMainModule generates the main entry module for an llgo program. // // The module contains argc/argv globals and, for executable build modes, // the entry function that wires initialization and main. For C archive or // shared library modes, only the globals are emitted. -func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, needRuntime, needPyInit, needAbiInit bool) Package { +func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *genConfig) Package { prog := ctx.prog mainPkg := prog.NewPackage("", pkg.ID+".main") @@ -76,32 +85,33 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, needRu defineWeakNoArgStub(mainPkg, "syscall.init") var pyInit llssa.Function - if needPyInit { + if cfg.pyInit { pyInit = declareNoArgFunc(mainPkg, "Py_Initialize") } var rtInit llssa.Function - if needRuntime { + if cfg.rtInit { rtInit = declareNoArgFunc(mainPkg, rtPkgPath+".init") } - if !IsDbgEnabled() && !needAbiInit { + if cfg.abiPrune { progSSA := ctx.progSSA chaGraph := cha.CallGraph(progSSA) - //vtaGraph := vta.CallGraph(ssautil.AllFunctions(progSSA), chaGraph) - //_ = vtaGraph invoked := buildInvokeIndex(chaGraph) - mainPkg.PruneAbiTypes(func(sel *types.Selection) bool { - method := progSSA.MethodValue(sel) - if _, ok := invoked[method]; ok { + mainPkg.PruneAbiTypes(cfg.abiInit, func(index int, method *types.Selection) bool { + if cfg.abiInit && ast.IsExported(method.Obj().Name()) { + return true + } + mth := progSSA.MethodValue(method) + if _, ok := invoked[mth]; ok { return true } for v := range invoked { - if v.Name() == method.Name() { - if !types.Identical(prog.PatchType(v.Type().(*types.Signature).Recv().Type()), sel.Type().(*types.Signature).Recv().Type()) { + if v.Name() == mth.Name() { + if !types.Identical(prog.Patch(v.Type().(*types.Signature).Recv().Type()), method.Type().(*types.Signature).Recv().Type()) { continue } - if !types.Identical(prog.PatchType(v.Type()), sel.Type()) { + if !types.Identical(prog.Patch(v.Type()), method.Type()) { continue } return true @@ -112,7 +122,7 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, needRu } var abiInit llssa.Function - if needAbiInit { + if cfg.abiInit { abiInit = mainPkg.InitAbiTypes("init$abitypes") } diff --git a/internal/build/main_module_test.go b/internal/build/main_module_test.go index c2ea97b585..d5cb7f704c 100644 --- a/internal/build/main_module_test.go +++ b/internal/build/main_module_test.go @@ -29,7 +29,7 @@ func TestGenMainModuleExecutable(t *testing.T) { }, } pkg := &packages.Package{PkgPath: "example.com/foo", ExportFile: "foo.a"} - mod := genMainModule(ctx, llssa.PkgRuntime, pkg, true, true, true) + mod := genMainModule(ctx, llssa.PkgRuntime, pkg, &genConfig{rtInit: true, pyInit: true, abiInit: true}) if mod.ExportFile != "foo.a-main" { t.Fatalf("unexpected export file: %s", mod.ExportFile) } @@ -59,7 +59,7 @@ func TestGenMainModuleLibrary(t *testing.T) { }, } pkg := &packages.Package{PkgPath: "example.com/foo", ExportFile: "foo.a"} - mod := genMainModule(ctx, llssa.PkgRuntime, pkg, false, false, false) + mod := genMainModule(ctx, llssa.PkgRuntime, pkg, &genConfig{}) ir := mod.LPkg.String() if strings.Contains(ir, "define i32 @main") { t.Fatalf("library mode should not emit main function:\n%s", ir) diff --git a/ssa/abitype.go b/ssa/abitype.go index 9e40f4d4a0..df158f22f2 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -427,7 +427,7 @@ func (b Builder) abiUncommonMethods(t types.Type, name string, mset *types.Metho } mSig := m.Type().(*types.Signature) var tfn, ifn llvm.Value - if prog.abiTypePruning && !prog.methodIsInvoke(m) { + if prog.abiTypePruning && !prog.methodIsInvoke(i, m) { tfn = prog.Nil(prog.VoidPtr()).impl ifn = tfn } else { @@ -565,9 +565,12 @@ func (p Package) getAbiTypes(name string) Expr { sort.Strings(names) fields := make([]llvm.Value, len(names)) for i, name := range names { - g := p.doNewVar(name, prog.abiSymbol[name].typ) - g.impl.SetLinkage(llvm.ExternalLinkage) - g.impl.SetGlobalConstant(true) + g := p.VarOf(name) + if g == nil { + g = p.doNewVar(name, prog.abiSymbol[name].typ) + g.impl.SetLinkage(llvm.ExternalLinkage) + g.impl.SetGlobalConstant(true) + } ptr := Expr{llvm.ConstGEP(g.impl.GlobalValueType(), g.impl, []llvm.Value{ llvm.ConstInt(prog.Int32().ll, 0, false), llvm.ConstInt(prog.Int32().ll, 0, false), @@ -605,7 +608,7 @@ func (p Package) InitAbiTypes(fname string) Function { return initFn } -func (p Package) PruneAbiTypes(methodIsInvoke func(method *types.Selection) bool) { +func (p Package) PruneAbiTypes(needAbiInit bool, methodIsInvoke func(index int, method *types.Selection) bool) { prog := p.Prog var names []string for k, sym := range prog.abiSymbol { @@ -615,10 +618,14 @@ func (p Package) PruneAbiTypes(methodIsInvoke func(method *types.Selection) bool names = append(names, k) } sort.Strings(names) - p.SetResolveLinkname(prog.resolveLinkname) + p.SetResolveLinkname(prog.ResolveLinkname) if methodIsInvoke == nil { - methodIsInvoke = func(method *types.Selection) bool { - if ms, ok := prog.invokeMethods[method.Obj().Name()]; ok { + methodIsInvoke = func(index int, method *types.Selection) bool { + name := method.Obj().Name() + if needAbiInit && ast.IsExported(name) { + return true + } + if ms, ok := prog.invokeMethods[name]; ok { for m := range ms { if types.Identical(m, method.Type()) { return true diff --git a/ssa/package.go b/ssa/package.go index be45174593..b224082335 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -216,7 +216,7 @@ type aProgram struct { abiSymbol map[string]*AbiSymbol // abi symbol name => Type abiTypeName map[types.Type]string abiTypePruning bool - methodIsInvoke func(method *types.Selection) bool + methodIsInvoke func(index int, method *types.Selection) bool invokeMethods map[string]map[types.Type]none @@ -234,7 +234,7 @@ func (p Program) AddInvoke(fn *types.Func) { m = make(map[types.Type]none) p.invokeMethods[name] = m } - m[p.patch(fn.Type())] = none{} + m[p.Patch(fn.Type())] = none{} } // A Program presents a program. @@ -306,11 +306,7 @@ func (p Program) SetPatch(patchType func(types.Type) types.Type) { p.patchType = patchType } -func (p Program) PatchType(typ types.Type) types.Type { - return p.patch(typ) -} - -func (p Program) patch(typ types.Type) types.Type { +func (p Program) Patch(typ types.Type) types.Type { if p.patchType != nil { return p.patchType(typ) } @@ -345,7 +341,7 @@ func (p Program) Linkname(name string) (link string, ok bool) { return } -func (p Program) resolveLinkname(name string) string { +func (p Program) ResolveLinkname(name string) string { if link, ok := p.linkname[name]; ok { prefix, ltarget, _ := strings.Cut(link, ".") if prefix != "C" { diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index d04a7c04e3..b31ec80658 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -1330,7 +1330,7 @@ func TestAbiPrune(t *testing.T) { bNE.Return(bNE.MakeInterface(nonEmptyType, prog.Zero(prog.Type(named, InGo)))) mainpkg := prog.NewPackage("main", "") - mainpkg.PruneAbiTypes(nil) + mainpkg.PruneAbiTypes(false, nil) s := mainpkg.String() if !strings.Contains(s, `@"*_llgo_foo/bar.Point" = constant { %"github.com/goplus/llgo/runtime/abi.PtrType",`) { t.Fatal("error puretype", s) @@ -1387,7 +1387,8 @@ func TestAbiTables(t *testing.T) { bNE := fnNE.MakeBody(1) bNE.Return(bNE.MakeInterface(nonEmptyType, prog.Val(7))) - fn := pkg.InitAbiTypes(pkg.Path() + ".init$abitables") + mainpkg := prog.NewPackage("main", "") + fn := mainpkg.InitAbiTypes(pkg.Path() + ".init$abitables") s := fn.impl.String() if !strings.Contains(s, `define void @"foo/bar.init$abitables"() { _llgo_0: @@ -1398,3 +1399,66 @@ _llgo_0: t.Fatal("error abi tables", s) } } + +func TestResolveLinkname(t *testing.T) { + tests := []struct { + name string + link map[string]string + input string + want string + panics bool + }{ + { + name: "Normal", + link: map[string]string{ + "foo": "C.bar", + }, + input: "foo", + want: "bar", + }, + { + name: "MultipleLinks", + link: map[string]string{ + "foo1": "C.bar1", + "foo2": "C.bar2", + }, + input: "foo2", + want: "bar2", + }, + { + name: "NoLink", + link: map[string]string{}, + input: "foo", + want: "foo", + }, + { + name: "InvalidLink", + link: map[string]string{ + "foo": "invalid.bar", + }, + input: "foo", + panics: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.panics { + defer func() { + if r := recover(); r == nil { + t.Error("want panic") + } + }() + } + prog := NewProgram(nil) + for k, v := range tt.link { + prog.SetLinkname(k, v) + } + got := prog.ResolveLinkname(tt.input) + if !tt.panics { + if got != tt.want { + t.Errorf("got %q, want %q", got, tt.want) + } + } + }) + } +} diff --git a/ssa/type.go b/ssa/type.go index 522c1e2d3b..20f03e4d9a 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -447,7 +447,7 @@ func (p Program) toLLVMFields(raw *types.Struct) (fields []llvm.Type) { if n > 0 { fields = make([]llvm.Type, n) for i := 0; i < n; i++ { - fields[i] = p.rawType(p.patch(raw.Field(i).Type())).ll + fields[i] = p.rawType(p.Patch(raw.Field(i).Type())).ll } } return @@ -461,7 +461,7 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) { if n > 0 { ret = make([]llvm.Type, n) for i := 0; i < n; i++ { - ret[i] = p.rawType(p.patch(t.At(i).Type())).ll + ret[i] = p.rawType(p.Patch(t.At(i).Type())).ll } } return From 8cd9aa017904fe79cd35a5759d21b9c1745e6f06 Mon Sep 17 00:00:00 2001 From: visualfc Date: Mon, 2 Mar 2026 07:17:48 +0800 Subject: [PATCH 3/5] internal/build: doc for callgraph RTA --- internal/build/main_module.go | 39 ++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/internal/build/main_module.go b/internal/build/main_module.go index 8d10330a1a..229a50129c 100644 --- a/internal/build/main_module.go +++ b/internal/build/main_module.go @@ -31,13 +31,13 @@ import ( "go/token" "go/types" + "golang.org/x/tools/go/callgraph/cha" + "github.com/goplus/llgo/internal/packages" + llssa "github.com/goplus/llgo/ssa" llvm "github.com/goplus/llvm" "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/callgraph/cha" "golang.org/x/tools/go/ssa" - - llssa "github.com/goplus/llgo/ssa" ) // genConfig controls the code generation behavior for the main module. @@ -96,8 +96,13 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g if cfg.abiPrune { progSSA := ctx.progSSA - chaGraph := cha.CallGraph(progSSA) - invoked := buildInvokeIndex(chaGraph) + /* + // RTA has issues parsing the patch package + res := buildRTAResult(progSSA) + invoked := buildInvokeIndex(res.CallGraph) + */ + cg := cha.CallGraph(progSSA) + invoked := buildInvokeIndex(cg) mainPkg.PruneAbiTypes(cfg.abiInit, func(index int, method *types.Selection) bool { if cfg.abiInit && ast.IsExported(method.Obj().Name()) { return true @@ -106,12 +111,14 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g if _, ok := invoked[mth]; ok { return true } + mtyp := method.Type() for v := range invoked { if v.Name() == mth.Name() { - if !types.Identical(prog.Patch(v.Type().(*types.Signature).Recv().Type()), method.Type().(*types.Signature).Recv().Type()) { + vtyp := v.Type() + if !types.Identical(prog.Patch(vtyp.(*types.Signature).Recv().Type()), mtyp.(*types.Signature).Recv().Type()) { continue } - if !types.Identical(prog.Patch(v.Type()), method.Type()) { + if !types.Identical(prog.Patch(vtyp), mtyp) { continue } return true @@ -138,6 +145,24 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g return mainAPkg } +/* +func buildRTAResult(progSSA *ssa.Program) *rta.Result { + var roots []*ssa.Function + for _, pkg := range progSSA.AllPackages() { + if pkg.Pkg.Name() == "main" { + if fn := pkg.Func("main"); fn != nil { + roots = append(roots, fn) + } + } + if fn := pkg.Func("init"); fn != nil { + roots = append(roots, fn) + } + } + res := rta.Analyze(roots, true) + return res +} +*/ + func buildInvokeIndex(cg *callgraph.Graph) map[*ssa.Function]bool { invoked := make(map[*ssa.Function]bool) for _, node := range cg.Nodes { From 47f647088fa2d37ef522418e6c7b1fb019042727 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 28 Feb 2026 16:38:47 +0800 Subject: [PATCH 4/5] internal/build: prune abi use RTA --- internal/build/main_module.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/build/main_module.go b/internal/build/main_module.go index 229a50129c..fb549c7b48 100644 --- a/internal/build/main_module.go +++ b/internal/build/main_module.go @@ -31,12 +31,11 @@ import ( "go/token" "go/types" - "golang.org/x/tools/go/callgraph/cha" - "github.com/goplus/llgo/internal/packages" llssa "github.com/goplus/llgo/ssa" llvm "github.com/goplus/llvm" "golang.org/x/tools/go/callgraph" + "golang.org/x/tools/go/callgraph/cha" "golang.org/x/tools/go/ssa" ) From 6eb60516382fae58b6c56e716516b665bd5fadcd Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 12 Feb 2026 16:56:29 +0800 Subject: [PATCH 5/5] ssa: check abiInit flag for abiInitList/abiPrune --- internal/build/build.go | 23 ++++++---- internal/build/main_module.go | 32 ++++++++++---- internal/build/main_module_test.go | 2 +- ssa/abitype.go | 50 ++++++++++++++++----- ssa/expr.go | 71 +++++++++++++++++++++++++++--- ssa/package.go | 22 +++++++-- ssa/ssa_test.go | 4 +- 7 files changed, 165 insertions(+), 39 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index 0409c9bb41..a1d0d476ba 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -889,7 +889,9 @@ func compileExtraFiles(ctx *context, verbose bool) ([]string, error) { func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPath string, verbose bool) error { needRuntime := false needPyInit := false - needAbiInit := false + var needAbiInit int + methodByIndex := make(map[int]none) + methodByName := make(map[string]none) allPkgs := []*packages.Package{pkg} for _, v := range pkgs { allPkgs = append(allPkgs, v.Package) @@ -926,10 +928,13 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa needRuntime = needRuntime || need1 needPyInit = needPyInit || need2 } - if aPkg.LPkg.NeedAbiInit { - needAbiInit = true + needAbiInit |= aPkg.LPkg.NeedAbiInit + for k, _ := range aPkg.LPkg.MethodByIndex { + methodByIndex[k] = none{} + } + for k, _ := range aPkg.LPkg.MethodByName { + methodByName[k] = none{} } - linkArgs = append(linkArgs, aPkg.LinkArgs...) if aPkg.ArchiveFile != "" { linkInputs = append(linkInputs, aPkg.ArchiveFile) @@ -947,10 +952,12 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, outputPa // This is compiled directly to .o and added to linkInputs (not cached) // Use a stable synthetic name to avoid confusing it with the real main package in traces/logs. entryPkg := genMainModule(ctx, llssa.PkgRuntime, pkg, &genConfig{ - rtInit: needRuntime, - pyInit: needPyInit, - abiInit: needAbiInit, - abiPrune: !IsDbgEnabled(), + rtInit: needRuntime, + pyInit: needPyInit, + abiInit: needAbiInit, + abiPrune: !IsDbgEnabled(), + methodByIndex: methodByIndex, + methodByName: methodByName, }) entryObjFile, err := exportObject(ctx, "entry_main", entryPkg.ExportFile, []byte(entryPkg.LPkg.String())) if err != nil { diff --git a/internal/build/main_module.go b/internal/build/main_module.go index fb549c7b48..5eccb9b39a 100644 --- a/internal/build/main_module.go +++ b/internal/build/main_module.go @@ -41,10 +41,12 @@ import ( // genConfig controls the code generation behavior for the main module. type genConfig struct { - rtInit bool - pyInit bool - abiInit bool - abiPrune bool + rtInit bool + pyInit bool + abiInit int + abiPrune bool + methodByIndex map[int]none + methodByName map[string]none } // genMainModule generates the main entry module for an llgo program. @@ -103,8 +105,22 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g cg := cha.CallGraph(progSSA) invoked := buildInvokeIndex(cg) mainPkg.PruneAbiTypes(cfg.abiInit, func(index int, method *types.Selection) bool { - if cfg.abiInit && ast.IsExported(method.Obj().Name()) { - return true + name := method.Obj().Name() + if ast.IsExported(name) { + if cfg.abiInit&llssa.ReflectMethodDynamic != 0 { + return true + } else { + if cfg.abiInit&llssa.ReflectMethodByIndex != 0 { + if _, ok := cfg.methodByIndex[index]; ok { + return true + } + } + if cfg.abiInit&llssa.ReflectMethodByName != 0 { + if _, ok := cfg.methodByName[name]; ok { + return true + } + } + } } mth := progSSA.MethodValue(method) if _, ok := invoked[mth]; ok { @@ -128,8 +144,8 @@ func genMainModule(ctx *context, rtPkgPath string, pkg *packages.Package, cfg *g } var abiInit llssa.Function - if cfg.abiInit { - abiInit = mainPkg.InitAbiTypes("init$abitypes") + if cfg.abiInit != 0 { + abiInit = mainPkg.InitAbiTypes(cfg.abiInit, "init$abitypes") } mainInit := declareNoArgFunc(mainPkg, pkg.PkgPath+".init") diff --git a/internal/build/main_module_test.go b/internal/build/main_module_test.go index d5cb7f704c..61feff3746 100644 --- a/internal/build/main_module_test.go +++ b/internal/build/main_module_test.go @@ -29,7 +29,7 @@ func TestGenMainModuleExecutable(t *testing.T) { }, } pkg := &packages.Package{PkgPath: "example.com/foo", ExportFile: "foo.a"} - mod := genMainModule(ctx, llssa.PkgRuntime, pkg, &genConfig{rtInit: true, pyInit: true, abiInit: true}) + mod := genMainModule(ctx, llssa.PkgRuntime, pkg, &genConfig{rtInit: true, pyInit: true, abiInit: llssa.ReflectMethodDynamic}) if mod.ExportFile != "foo.a-main" { t.Fatalf("unexpected export file: %s", mod.ExportFile) } diff --git a/ssa/abitype.go b/ssa/abitype.go index df158f22f2..e9ab404250 100644 --- a/ssa/abitype.go +++ b/ssa/abitype.go @@ -554,13 +554,43 @@ type AbiSymbol struct { uncommon bool } -func (p Package) getAbiTypes(name string) Expr { +func (p Package) getAbiTypes(abiInit int, name string) Expr { prog := p.Prog - names := make([]string, len(prog.abiSymbol)) - n := 0 - for k, _ := range prog.abiSymbol { - names[n] = k - n++ + var names []string + for k, sym := range prog.abiSymbol { + switch sym.raw.(type) { + case *types.Array: + if abiInit&ReflectArrayOf == 0 { + continue + } + case *types.Chan: + if abiInit&ReflectChanOf == 0 { + continue + } + case *types.Signature: + if abiInit&ReflectFuncOf == 0 && abiInit&ReflectMethodMask == 0 { + continue + } + case *types.Map: + if abiInit&ReflectMapOf == 0 { + continue + } + case *types.Pointer: + if abiInit&ReflectPointerTo == 0 { + continue + } + case *types.Slice: + if abiInit&ReflectSliceOf == 0 { + continue + } + case *types.Struct: + if abiInit&ReflectStructOf == 0 { + continue + } + default: + continue + } + names = append(names, k) } sort.Strings(names) fields := make([]llvm.Value, len(names)) @@ -595,7 +625,7 @@ func (p Package) getAbiTypes(name string) Expr { return g.Expr } -func (p Package) InitAbiTypes(fname string) Function { +func (p Package) InitAbiTypes(abiInit int, fname string) Function { if len(p.Prog.abiSymbol) == 0 { return nil } @@ -603,12 +633,12 @@ func (p Package) InitAbiTypes(fname string) Function { initFn := p.NewFunc(fname, NoArgsNoRet, InC) b := initFn.MakeBody(1) g := p.NewVarEx(PkgRuntime+".typelist", prog.Pointer(prog.Slice(prog.AbiTypePtr()))) - b.Store(g.Expr, b.Load(p.getAbiTypes(fname))) + b.Store(g.Expr, b.Load(p.getAbiTypes(abiInit, fname))) b.Return() return initFn } -func (p Package) PruneAbiTypes(needAbiInit bool, methodIsInvoke func(index int, method *types.Selection) bool) { +func (p Package) PruneAbiTypes(needAbiInit int, methodIsInvoke func(index int, method *types.Selection) bool) { prog := p.Prog var names []string for k, sym := range prog.abiSymbol { @@ -622,7 +652,7 @@ func (p Package) PruneAbiTypes(needAbiInit bool, methodIsInvoke func(index int, if methodIsInvoke == nil { methodIsInvoke = func(index int, method *types.Selection) bool { name := method.Obj().Name() - if needAbiInit && ast.IsExported(name) { + if needAbiInit != 0 && ast.IsExported(name) { return true } if ms, ok := prog.invokeMethods[name]; ok { diff --git a/ssa/expr.go b/ssa/expr.go index 0e6daba9d7..4486f876f3 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -1081,27 +1081,84 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { default: log.Panicf("unreachable: %d(%T), %v\n", kind, raw, fn.RawType()) } - pkg := b.Pkg - if !pkg.NeedAbiInit && pkg.Path() != "reflect" { - if _, ok := reflectFunc[fn.Name()]; ok { - pkg.NeedAbiInit = true - } + if b.Pkg.Path() != "reflect" { + b.checkReflect(fn, args) } ret.Type = b.Prog.retType(sig) ret.impl = llvm.CreateCall(b.impl, ll, fn.impl, llvmParamsEx(data, args, sig.Params(), b)) return } +func (b Builder) checkReflect(fn Expr, args []Expr) { + pkg := b.Pkg + switch fn.Name() { + case "reflect.ArrayOf": + pkg.NeedAbiInit |= ReflectArrayOf + case "reflect.ChanOf": + pkg.NeedAbiInit |= ReflectChanOf + case "reflect.FuncOf": + pkg.NeedAbiInit |= ReflectFuncOf + case "reflect.MapOf": + pkg.NeedAbiInit |= ReflectMapOf + case "reflect.PointerTo", "reflect.PtrTo": + pkg.NeedAbiInit |= ReflectPointerTo + case "reflect.SliceOf": + pkg.NeedAbiInit |= ReflectSliceOf + case "reflect.StructOf": + pkg.NeedAbiInit |= ReflectStructOf + case "reflect.Value.Method": + if len(args) == 2 { + if v, ok := extractConstInt(args[1].impl); ok { + if pkg.MethodByIndex == nil { + pkg.MethodByIndex = make(map[int]none) + } + pkg.MethodByIndex[v] = none{} + pkg.NeedAbiInit |= ReflectMethodByIndex + return + } + } + pkg.NeedAbiInit |= ReflectMethodDynamic + case "reflect.Value.MethodByName": + if len(args) == 2 { + if v, ok := extractConstString(args[1].impl); ok { + if pkg.MethodByName == nil { + pkg.MethodByName = make(map[string]none) + } + pkg.MethodByName[v] = none{} + pkg.NeedAbiInit |= ReflectMethodByName + return + } + } + pkg.NeedAbiInit |= ReflectMethodDynamic + } +} + +func extractConstInt(v llvm.Value) (r int, ok bool) { + if rv := v.IsAConstantInt(); !rv.IsNil() { + return int(rv.SExtValue()), true + } + return +} + +func extractConstString(v llvm.Value) (str string, ok bool) { + if st := v.IsAConstantStruct(); !st.IsNil() { + if init := st.Operand(0).Initializer(); !init.IsNil() { + return init.ConstGetAsString(), true + } + } + return +} + var ( reflectFunc = map[string]struct{}{ "reflect.ArrayOf": {}, "reflect.ChanOf": {}, "reflect.FuncOf": {}, "reflect.MapOf": {}, - "reflect.SliceOf": {}, - "reflect.StructOf": {}, "reflect.PointerTo": {}, "reflect.PtrTo": {}, + "reflect.SliceOf": {}, + "reflect.StructOf": {}, "reflect.Value.Method": {}, "reflect.Value.MethodByName": {}, } diff --git a/ssa/package.go b/ssa/package.go index b224082335..e364c5d3ce 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -727,13 +727,29 @@ type aPackage struct { iRoutine int - NeedRuntime bool - NeedPyInit bool - NeedAbiInit bool // need load all abi types for reflect make type + NeedRuntime bool + NeedPyInit bool + NeedAbiInit int // need load all abi types for reflect make type + MethodByIndex map[int]none + MethodByName map[string]none export map[string]string // pkgPath.nameInPkg => exportname } +const ( + ReflectArrayOf = 1 << iota + ReflectChanOf + ReflectFuncOf + ReflectMapOf + ReflectPointerTo + ReflectSliceOf + ReflectStructOf + ReflectMethodByIndex + ReflectMethodByName + ReflectMethodDynamic + ReflectMethodMask = ReflectMethodByIndex | ReflectMethodByName | ReflectMethodDynamic +) + type Package = *aPackage func (p Package) Module() llvm.Module { diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index b31ec80658..a70385f7c4 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -1330,7 +1330,7 @@ func TestAbiPrune(t *testing.T) { bNE.Return(bNE.MakeInterface(nonEmptyType, prog.Zero(prog.Type(named, InGo)))) mainpkg := prog.NewPackage("main", "") - mainpkg.PruneAbiTypes(false, nil) + mainpkg.PruneAbiTypes(ReflectMethodDynamic, nil) s := mainpkg.String() if !strings.Contains(s, `@"*_llgo_foo/bar.Point" = constant { %"github.com/goplus/llgo/runtime/abi.PtrType",`) { t.Fatal("error puretype", s) @@ -1388,7 +1388,7 @@ func TestAbiTables(t *testing.T) { bNE.Return(bNE.MakeInterface(nonEmptyType, prog.Val(7))) mainpkg := prog.NewPackage("main", "") - fn := mainpkg.InitAbiTypes(pkg.Path() + ".init$abitables") + fn := mainpkg.InitAbiTypes(ReflectStructOf, pkg.Path()+".init$abitables") s := fn.impl.String() if !strings.Contains(s, `define void @"foo/bar.init$abitables"() { _llgo_0: