From b7b6e3bc93d2905f4713c0911f6a2a67bead85a9 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 23 Jun 2025 21:14:26 +0800 Subject: [PATCH 1/3] testFrom: use test.Diff --- .gitignore | 1 + cl/cltest/cltest.go | 15 ++++++++------- cl/compile.go | 9 +-------- cl/import.go | 2 +- cl/instr.go | 2 +- 5 files changed, 12 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 3d29051e..7c0b0d92 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ stories*.bin .DS_Store err.log numpy.txt +result.txt _go/ _runtime/ diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 1b6a82e4..25cc9bea 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -18,6 +18,7 @@ package cltest import ( "archive/zip" + "bytes" "go/ast" "go/parser" "go/token" @@ -35,6 +36,7 @@ import ( "github.com/goplus/llgo/cl" "github.com/goplus/llgo/internal/llgen" "github.com/goplus/llgo/ssa/ssatest" + "github.com/qiniu/x/test" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" @@ -112,14 +114,13 @@ func testFrom(t *testing.T, pkgDir, sel string) { return } log.Println("Parsing", pkgDir) + v := llgen.GenFrom(pkgDir) out := pkgDir + "/out.ll" - b, err := os.ReadFile(out) - if err != nil { - t.Fatal("ReadFile failed:", err) - } - expected := string(b) - if v := llgen.GenFrom(pkgDir); v != expected && expected != ";" { // expected == ";" means skipping out.ll - t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected) + b, _ := os.ReadFile(out) + if !bytes.Equal(b, []byte{';'}) { // expected == ";" means skipping out.ll + if test.Diff(t, pkgDir+"/result.txt", []byte(v), b) { + t.Fatal("llgen.GenFrom: unexpect result") + } } } diff --git a/cl/compile.go b/cl/compile.go index 8e9434eb..e8c3fdcb 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -213,15 +213,8 @@ func isCgoVar(name string) bool { } func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Function, llssa.PyObjRef, int) { - pkgTypes, name, ftype := p.funcName(f, true) + pkgTypes, name, ftype := p.funcName(f) if ftype != goFunc { - /* - if ftype == pyFunc { - // TODO(xsw): pyMod == "" - fnName := pysymPrefix + p.pyMod + "." + name - return nil, pkg.NewPyFunc(fnName, f.Signature, call), pyFunc - } - */ return nil, nil, ignoredFunc } sig := f.Signature diff --git a/cl/import.go b/cl/import.go index f5e48c0b..be3cc8c3 100644 --- a/cl/import.go +++ b/cl/import.go @@ -497,7 +497,7 @@ const ( llgoAtomicOpLast = llgoAtomicOpBase + int(llssa.OpUMin) ) -func (p *context) funcName(fn *ssa.Function, ignore bool) (*types.Package, string, int) { +func (p *context) funcName(fn *ssa.Function) (*types.Package, string, int) { var pkg *types.Package var orgName string if origin := fn.Origin(); origin != nil { diff --git a/cl/instr.go b/cl/instr.go index aebc8968..47a93df7 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -334,7 +334,7 @@ var llgoInstrs = map[string]int{ // funcOf returns a function by name and set ftype = goFunc, cFunc, etc. // or returns nil and set ftype = llgoCstr, llgoAlloca, llgoUnreachable, etc. func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObjRef, ftype int) { - pkgTypes, name, ftype := p.funcName(fn, false) + pkgTypes, name, ftype := p.funcName(fn) switch ftype { case pyFunc: if kind, mod := pkgKindByScope(pkgTypes.Scope()); kind == PkgPyModule { From 915cdf2c2343e94288dd587ae80f10caa7692b42 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 23 Jun 2025 22:01:42 +0800 Subject: [PATCH 2/3] #1165 --- cl/_testdata/cpkg/in.go | 13 ++++++++++ cl/_testdata/cpkg/out.ll | 35 ++++++++++++++++++++++++++ cl/compile.go | 2 +- cl/import.go | 54 ++++++++++++++++++++++++---------------- internal/build/build.go | 8 +++--- internal/build/clean.go | 2 ++ 6 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 cl/_testdata/cpkg/in.go create mode 100644 cl/_testdata/cpkg/out.ll diff --git a/cl/_testdata/cpkg/in.go b/cl/_testdata/cpkg/in.go new file mode 100644 index 00000000..742e83df --- /dev/null +++ b/cl/_testdata/cpkg/in.go @@ -0,0 +1,13 @@ +package C + +func Xadd(a, b int) int { + return add(a, b) +} + +func Double(x float64) float64 { + return 2 * x +} + +func add(a, b int) int { + return a + b +} diff --git a/cl/_testdata/cpkg/out.ll b/cl/_testdata/cpkg/out.ll new file mode 100644 index 00000000..934dc700 --- /dev/null +++ b/cl/_testdata/cpkg/out.ll @@ -0,0 +1,35 @@ +; ModuleID = 'github.com/goplus/llgo/cl/_testdata/cpkg' +source_filename = "github.com/goplus/llgo/cl/_testdata/cpkg" + +@"github.com/goplus/llgo/cl/_testdata/cpkg.init$guard" = global i1 false, align 1 + +define double @Double(double %0) { +_llgo_0: + %1 = fmul double 2.000000e+00, %0 + ret double %1 +} + +define i64 @add(i64 %0, i64 %1) { +_llgo_0: + %2 = call i64 @"github.com/goplus/llgo/cl/_testdata/cpkg.add"(i64 %0, i64 %1) + ret i64 %2 +} + +define i64 @"github.com/goplus/llgo/cl/_testdata/cpkg.add"(i64 %0, i64 %1) { +_llgo_0: + %2 = add i64 %0, %1 + ret i64 %2 +} + +define void @"github.com/goplus/llgo/cl/_testdata/cpkg.init"() { +_llgo_0: + %0 = load i1, ptr @"github.com/goplus/llgo/cl/_testdata/cpkg.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"github.com/goplus/llgo/cl/_testdata/cpkg.init$guard", align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} diff --git a/cl/compile.go b/cl/compile.go index e8c3fdcb..6f391f95 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -1005,7 +1005,7 @@ func NewPackageEx(prog llssa.Program, patches Patches, pkg *ssa.Package, files [ cgoSymbols: make([]string, 0, 128), } ctx.initPyModule() - ctx.initFiles(pkgPath, files) + ctx.initFiles(pkgPath, files, pkgName == "C") ctx.prog.SetPatch(ctx.patchType) ret.SetPatch(ctx.patchType) ret.SetResolveLinkname(ctx.resolveLinkname) diff --git a/cl/import.go b/cl/import.go index be3cc8c3..b70d0046 100644 --- a/cl/import.go +++ b/cl/import.go @@ -174,13 +174,18 @@ start: syms.initLinknames(p) } -func (p *context) initFiles(pkgPath string, files []*ast.File) { +func (p *context) initFiles(pkgPath string, files []*ast.File, cPkg bool) { for _, file := range files { for _, decl := range file.Decls { switch decl := decl.(type) { case *ast.FuncDecl: fullName, inPkgName := astFuncName(pkgPath, decl) - p.initLinknameByDoc(decl.Doc, fullName, inPkgName, false) + if !p.initLinknameByDoc(decl.Doc, fullName, inPkgName, false) && cPkg { + // package C (https://github.com/goplus/llgo/issues/1165) + if decl.Recv == nil && token.IsExported(inPkgName) { + p.prog.SetLinkname(fullName, strings.TrimPrefix(inPkgName, "X")) + } + } case *ast.GenDecl: switch decl.Tok { case token.VAR: @@ -266,51 +271,58 @@ func (p *context) collectSkip(line string, prefix int) { } } -func (p *context) initLinknameByDoc(doc *ast.CommentGroup, fullName, inPkgName string, isVar bool) { +func (p *context) initLinknameByDoc(doc *ast.CommentGroup, fullName, inPkgName string, isVar bool) bool { if doc != nil { for n := len(doc.List) - 1; n >= 0; n-- { line := doc.List[n].Text - found := p.initLinkname(line, func(name string) (_ string, _, ok bool) { + ret := p.initLinkname(line, func(name string) (_ string, _, ok bool) { return fullName, isVar, name == inPkgName }) - if !found { - break + if ret != unknownDirective { + return ret == hasLinkname } } } + return false } -func (p *context) initLinkname(line string, f func(inPkgName string) (fullName string, isVar, ok bool)) bool { +const ( + noDirective = iota + hasLinkname + unknownDirective = -1 +) + +func (p *context) initLinkname(line string, f func(inPkgName string) (fullName string, isVar, ok bool)) int { const ( - linkname = "//go:linkname " - llgolink = "//llgo:link " - llgolink2 = "// llgo:link " - exportName = "//export " - directive = "//go:" + linkname = "//go:linkname " + llgolink = "//llgo:link " + llgolink2 = "// llgo:link " + export = "//export " + directive = "//go:" ) if strings.HasPrefix(line, linkname) { p.initLink(line, len(linkname), f) - return true + return hasLinkname } else if strings.HasPrefix(line, llgolink2) { p.initLink(line, len(llgolink2), f) - return true + return hasLinkname } else if strings.HasPrefix(line, llgolink) { p.initLink(line, len(llgolink), f) - return true - } else if strings.HasPrefix(line, exportName) { - p.initCgoExport(line, len(exportName), f) - return true + return hasLinkname + } else if strings.HasPrefix(line, export) { + p.initCgoExport(line, len(export), f) + return hasLinkname } else if strings.HasPrefix(line, directive) { // skip unknown annotation but continue to parse the next annotation - return true + return unknownDirective } - return false + return noDirective } func (p *context) initCgoExport(line string, prefix int, f func(inPkgName string) (fullName string, isVar, ok bool)) { name := strings.TrimSpace(line[prefix:]) if fullName, _, ok := f(name); ok { - p.cgoExports[fullName] = name + p.cgoExports[fullName] = name // TODO(xsw): why not use prog.SetLinkname? } } diff --git a/internal/build/build.go b/internal/build/build.go index 4b47b3b1..3c45dac1 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -184,7 +184,7 @@ func Do(args []string, conf *Config) ([]Package, error) { prog := llssa.NewProgram(target) sizes := func(sizes types.Sizes, compiler, arch string) types.Sizes { if arch == "wasm" { - sizes = &types.StdSizes{4, 4} + sizes = &types.StdSizes{WordSize: 4, MaxAlign: 4} } return prog.TypeSizes(sizes) } @@ -803,7 +803,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { fmt.Fprintf(os.Stderr, "==> Export %s: %s\n", aPkg.PkgPath, pkg.ExportFile) } if IsCheckEnable() { - if err, msg := llcCheck(ctx.env, pkg.ExportFile); err != nil { + if msg, err := llcCheck(ctx.env, pkg.ExportFile); err != nil { fmt.Fprintf(os.Stderr, "==> lcc %v: %v\n%v\n", pkg.PkgPath, pkg.ExportFile, msg) } } @@ -811,7 +811,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { return nil } -func llcCheck(env *llvm.Env, exportFile string) (err error, msg string) { +func llcCheck(env *llvm.Env, exportFile string) (msg string, err error) { bin := filepath.Join(env.BinDir(), "llc") cmd := exec.Command(bin, "-filetype=null", exportFile) var buf bytes.Buffer @@ -904,6 +904,7 @@ func createSSAPkg(prog *ssa.Program, p *packages.Package, verbose bool) *ssa.Pac return pkgSSA } +/* var ( // TODO(xsw): complete build flags buildFlags = map[string]bool{ @@ -922,6 +923,7 @@ var ( "-ldflags": true, // --ldflags 'flag list': arguments to pass on each go tool link invocation } ) +*/ const llgoDebug = "LLGO_DEBUG" const llgoDbgSyms = "LLGO_DEBUG_SYMBOLS" diff --git a/internal/build/clean.go b/internal/build/clean.go index 71ca1a1f..31fecf18 100644 --- a/internal/build/clean.go +++ b/internal/build/clean.go @@ -26,12 +26,14 @@ import ( "github.com/goplus/llgo/internal/packages" ) +/* var ( // TODO(xsw): complete clean flags cleanFlags = map[string]bool{ "-v": false, // -v: print the paths of packages as they are clean } ) +*/ func Clean(patterns []string, conf *Config) { if conf.Goos == "" { From 84f0c0fd9fb41e7e7fa1674bc9b852ed3cf7e8b9 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 23 Jun 2025 22:43:30 +0800 Subject: [PATCH 3/3] codecov: skip cl/cltest --- .github/codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/codecov.yml b/.github/codecov.yml index b59ee2db..a9f23950 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -2,6 +2,7 @@ coverage: ignore: - "chore" - "cmd" + - "cl/cltest" - "internal/build" - "internal/llgen" - "internal/mockable"