From a71dc51acb8cd63dd9ce620870a13de62924239b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 12 Sep 2026 20:53:03 +0800 Subject: [PATCH 1/8] cl: ctx.getPubName --- cl/_mod/go.mod | 5 ----- cl/_mod/go.sum | 2 -- cl/_mod/stub.go | 3 --- cl/_testmockc/function/in.h | 2 +- cl/_testmockc/function/out.go | 6 +++--- cl/blockctx.go | 18 ++++++++++++++++-- cl/compile.go | 3 ++- 7 files changed, 22 insertions(+), 17 deletions(-) delete mode 100644 cl/_mod/go.mod delete mode 100644 cl/_mod/go.sum delete mode 100644 cl/_mod/stub.go diff --git a/cl/_mod/go.mod b/cl/_mod/go.mod deleted file mode 100644 index 87fb2ade..00000000 --- a/cl/_mod/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module foo - -go 1.20 - -require github.com/goplus/lib v0.5.2 diff --git a/cl/_mod/go.sum b/cl/_mod/go.sum deleted file mode 100644 index 8fad59c3..00000000 --- a/cl/_mod/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/goplus/lib v0.5.2 h1:BUd3mUwTajDRBHVxMfS/y/hDJ6n/Pxwf6z7ikrOXvkE= -github.com/goplus/lib v0.5.2/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0= diff --git a/cl/_mod/stub.go b/cl/_mod/stub.go deleted file mode 100644 index 1b5378c5..00000000 --- a/cl/_mod/stub.go +++ /dev/null @@ -1,3 +0,0 @@ -package foo - -import _ "github.com/goplus/lib/c" diff --git a/cl/_testmockc/function/in.h b/cl/_testmockc/function/in.h index 98a8f3dd..b58d01e0 100644 --- a/cl/_testmockc/function/in.h +++ b/cl/_testmockc/function/in.h @@ -1,5 +1,5 @@ unsigned f(int a); -void g(); +void _g(); signed int xprintf(const char* fmt, ...); diff --git a/cl/_testmockc/function/out.go b/cl/_testmockc/function/out.go index 14ac025a..2e23948c 100644 --- a/cl/_testmockc/function/out.go +++ b/cl/_testmockc/function/out.go @@ -2,6 +2,6 @@ package foo import "github.com/goplus/lib/c" -func f(a c.Int) c.Uint -func g() -func xprintf(fmt *c.Char, __llgo_va_list ...any) c.Int +func F(a c.Int) c.Uint +func X_g() +func Xprintf(fmt *c.Char, __llgo_va_list ...any) c.Int diff --git a/cl/blockctx.go b/cl/blockctx.go index 8f79adf8..de8a9d45 100644 --- a/cl/blockctx.go +++ b/cl/blockctx.go @@ -89,9 +89,23 @@ func (p *blockCtx) initFile(file Source) { } func (p *blockCtx) getPubName(pfnName *string) (rewritten bool) { - // TODO(xsw): - _ = pfnName + fnName := *pfnName + pubName := cPubName(fnName) + rewritten = fnName != pubName + if rewritten { + *pfnName = pubName + } return } +func cPubName(name string) string { + if r := name[0]; 'a' <= r && r <= 'z' { + r -= 'a' - 'A' + return string(r) + name[1:] + } else if r == '_' { + return "X" + name + } + return name +} + // ----------------------------------------------------------------------------- diff --git a/cl/compile.go b/cl/compile.go index 1227d5b3..dd2948eb 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -77,7 +77,8 @@ type Config struct { // Reused specifies to reuse the Package instance between processing multiple C/C++ header files. *Reused - // NameLookup looks up the archive path for a given mangling name. It returns the archive path and a boolean indicating whether the lookup was successful. + // NameLookup looks up the archive path for a given mangling name. It returns the archive + // path and a boolean indicating whether the lookup was successful. NameLookup func(manglingName string) (archivePath string, ok bool) } From dcece70a1d708bc46ea3b84e3e275e388a34254d Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 12 Sep 2026 21:03:29 +0800 Subject: [PATCH 2/8] cl: compileFunc SetComments //go:linkname --- cl/_testmockc/function/out.go | 5 +++++ cl/compile.go | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/cl/_testmockc/function/out.go b/cl/_testmockc/function/out.go index 2e23948c..6e8020f5 100644 --- a/cl/_testmockc/function/out.go +++ b/cl/_testmockc/function/out.go @@ -2,6 +2,11 @@ package foo import "github.com/goplus/lib/c" +//go:linkname F C.f func F(a c.Int) c.Uint + +//go:linkname X_g C._g func X_g() + +//go:linkname Xprintf C.xprintf func Xprintf(fmt *c.Char, __llgo_va_list ...any) c.Int diff --git a/cl/compile.go b/cl/compile.go index dd2948eb..6c96dafa 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -17,6 +17,7 @@ package cl import ( + "go/ast" "go/token" "go/types" "log" @@ -235,6 +236,11 @@ func compileFunc(ctx *blockCtx, fn clang.Cursor) { if err != nil { log.Panicln("compileFunc:", fnName, err) } + f.SetComments(pkg, &ast.CommentGroup{ + List: []*ast.Comment{ + {Text: "\n//go:linkname " + fnName + " C." + origName}, + }, + }) // ctx.addExternFunc(fnName) if rewritten { scope := pkg.Types.Scope() @@ -342,7 +348,7 @@ func newParam(ctx *blockCtx, decl clang.Cursor, i c.Int) *types.Var { if declName != "" { avoidKeyword(&declName) } else { - declName = "__llcppg_param" + strconv.Itoa(int(i)+1) + declName = "_llcppg_param" + strconv.Itoa(int(i)+1) } return types.NewParam(goNodePos(ctx, decl), ctx.pkg.Types, declName, typ) } From 62bcf34f945144134ec3319c776ab48fd6bf8dca Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 12 Sep 2026 21:08:44 +0800 Subject: [PATCH 3/8] cl: ctx.forceImportUnsafe --- cl/_testmockc/function/out.go | 5 ++++- cl/blockctx.go | 9 +++++++++ cl/compile.go | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cl/_testmockc/function/out.go b/cl/_testmockc/function/out.go index 6e8020f5..7824fcd1 100644 --- a/cl/_testmockc/function/out.go +++ b/cl/_testmockc/function/out.go @@ -1,6 +1,9 @@ package foo -import "github.com/goplus/lib/c" +import ( + "github.com/goplus/lib/c" + _ "unsafe" +) //go:linkname F C.f func F(a c.Int) c.Uint diff --git a/cl/blockctx.go b/cl/blockctx.go index de8a9d45..e3a93df4 100644 --- a/cl/blockctx.go +++ b/cl/blockctx.go @@ -80,6 +80,15 @@ type blockCtx struct { fset *token.FileSet file *token.File c gogen.PkgRef + + unsafeImported bool +} + +func (p *blockCtx) forceImportUnsafe() { + if !p.unsafeImported { + p.unsafeImported = true + p.pkg.ForceImport("unsafe") + } } func (p *blockCtx) initFile(file Source) { diff --git a/cl/compile.go b/cl/compile.go index 6c96dafa..ca80978c 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -236,6 +236,7 @@ func compileFunc(ctx *blockCtx, fn clang.Cursor) { if err != nil { log.Panicln("compileFunc:", fnName, err) } + ctx.forceImportUnsafe() f.SetComments(pkg, &ast.CommentGroup{ List: []*ast.Comment{ {Text: "\n//go:linkname " + fnName + " C." + origName}, From aec817e2c3a76934fc399af7a9688672d78077f0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 12 Sep 2026 21:25:35 +0800 Subject: [PATCH 4/8] cl: use nameLookup to lookup manglingName --- cl/blockctx.go | 2 + cl/compile.go | 143 +++++-------------------------------------------- 2 files changed, 14 insertions(+), 131 deletions(-) diff --git a/cl/blockctx.go b/cl/blockctx.go index e3a93df4..a819963d 100644 --- a/cl/blockctx.go +++ b/cl/blockctx.go @@ -81,6 +81,8 @@ type blockCtx struct { file *token.File c gogen.PkgRef + nameLookup func(manglingName string) (archivePath string, ok bool) + unsafeImported bool } diff --git a/cl/compile.go b/cl/compile.go index ca80978c..cce5ac61 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -129,9 +129,9 @@ func loadFile(p *gogen.Package, conf *Config, file Source) (pi *PkgInfo, err err c := p.Import("github.com/goplus/lib/c") ctx := &blockCtx{ pkg: p, cb: p.CB(), fset: p.Fset, c: c, + nameLookup: conf.NameLookup, } ctx.initFile(file) - _ = conf clang.VisitChildren(file.TU.Cursor(), func(decl, parent clang.Cursor) clang.ChildVisitResult { compileDecl(ctx, decl) return clang.Continue @@ -152,52 +152,7 @@ func compileDecl(ctx *blockCtx, decl clang.Cursor) { case lc.CursorVarDecl: // compileVarDecl(ctx, decl, global) case lc.CursorTypedefDecl: - /* origName, pub := decl.Name, false - if global { - pub = ctx.getPubName(&decl.Name) - } - compileTypedef(ctx, decl, global, pub) - if pub { - substObj(ctx.pkg.Types, scope, origName, scope.Lookup(decl.Name)) - } - case ast.RecordDecl: - pub := false - name, suKind := ctx.getSuName(decl, decl.TagUsed) - origName := name - if global { - if suKind == suAnonymous { - // pub = true if this is a public typedef - pub = i+1 < n && isPubTypedef(ctx, node.Inner[i+1]) - } else { - pub = ctx.getPubName(&name) - if decl.CompleteDefinition && ctx.checkExists(name) { - continue - } - } - } - typ, del := compileStructOrUnion(ctx, name, decl, pub) - if suKind != suAnonymous { - if pub { - substObj(ctx.pkg.Types, scope, origName, scope.Lookup(name)) - } - break - } - ctx.unnameds[decl.ID] = unnamedType{typ: typ, del: del} - for i+1 < n { - next := node.Inner[i+1] - if next.Kind == ast.VarDecl { - if ret, ok := checkAnonymous(ctx, scope, typ, next); ok { - compileVarWith(ctx, ret, next) - i++ - continue - } - } - break - } - case ast.EmptyDecl: - case ast.StaticAssertDecl: - continue - */ + // TODO(xsw) case lc.CursorEnumDecl: // compileEnum(ctx, decl, global) default: @@ -207,7 +162,16 @@ func compileDecl(ctx *blockCtx, decl clang.Cursor) { // TODO(xsw): method support func compileFunc(ctx *blockCtx, fn clang.Cursor) { + m := fn.Mangling() + manglingName := c.GoString(m.CStr()) + m.Dispose() + fnName := clang.String(fn) + if _, ok := ctx.nameLookup(manglingName); !ok { + log.Println("func", fnName, "- skipped") + return + } + if debugCompileDecl { log.Println("func", fnName, "-", clang.String(fn.Type())) } @@ -239,96 +203,13 @@ func compileFunc(ctx *blockCtx, fn clang.Cursor) { ctx.forceImportUnsafe() f.SetComments(pkg, &ast.CommentGroup{ List: []*ast.Comment{ - {Text: "\n//go:linkname " + fnName + " C." + origName}, + {Text: "\n//go:linkname " + fnName + " C." + manglingName[1:]}, }, }) - // ctx.addExternFunc(fnName) if rewritten { scope := pkg.Types.Scope() substObj(pkg.Types, scope, origName, f) } - /* origName, rewritten := fnName, false - if !ctx.inHeader && fn.StorageClass == ast.Static { - fnName, rewritten = ctx.autoStaticName(origName), true - } else { - rewritten = ctx.getPubName(&fnName) - } - if body != nil { - if ctx.checkExists(fnName) { - return - } - isMain := false - if fnName == "main" && (results != nil || params != nil) { - fnName, isMain = "_cgo_main", true - } - f, err := pkg.NewFuncWith(ctx.goNodePos(fn), fnName, sig, nil) - if err != nil { - log.Panicln("compileFunc:", err) - } - if rewritten { // for fnName is a recursive function - scope := pkg.Types.Scope() - substObj(pkg.Types, scope, origName, f.Obj()) - rewritten = false - } - cb := f.BodyStart(pkg) - ctx.curfn = newFuncCtx(pkg, ctx.markComplicated(fnName, body), origName) - compileSub(ctx, body) - checkNeedReturn(ctx, body) - ctx.curfn = nil - cb.End() - if isMain { - var t *types.Var - var entryParams *types.Tuple - var entry = "main" - var testMain = ctx.testMain - if testMain { - entry = "TestMain" - testing := pkg.Import("testing") - t = pkg.NewParam(token.NoPos, "t", types.NewPointer(testing.Ref("T").Type())) - entryParams = types.NewTuple(t) - } - pkg.NewFunc(nil, entry, entryParams, nil, false).BodyStart(pkg) - if results != nil { - if testMain { - // if _cgo_ret := _cgo_main(); _cgo_ret != 0 { - // t.Fatal("exit status", _cgo_ret) - // } - cb.If().DefineVarStart(token.NoPos, retName) - } else { - // os.Exit(int(_cgo_main())) - cb.Val(pkg.Import("os").Ref("Exit")).Typ(types.Typ[types.Int]) - } - } - cb.Val(f.Obj()) - if params != nil { - panic("TODO: main func with params") - } - cb.Call(len(params)) - if results != nil { - if testMain { - cb.EndInit(1) - ret := cb.Scope().Lookup(retName) - cb.Val(ret).Val(0).BinaryOp(token.NEQ).Then(). - Val(t).MemberVal("Fatal").Val("exit status").Val(ret).Call(2).EndStmt(). - End() - } else { - cb.Call(1).Call(1) - } - } - cb.EndStmt().End() - } else { - delete(ctx.extfns, fnName) - } - } else if fn.IsUsed { - f := types.NewFunc(ctx.goNodePos(fn), pkg.Types, fnName, sig) - if pkg.Types.Scope().Insert(f) == nil { - ctx.addExternFunc(fnName) - } - } - if rewritten { - scope := pkg.Types.Scope() - substObj(pkg.Types, scope, origName, scope.Lookup(fnName)) - } */ } var ( From 3b84f3a948a7afdf7c0498a837ed3995048b010a Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 12 Sep 2026 21:48:15 +0800 Subject: [PATCH 5/8] cl compileFunc: echo skipped func only if debugCompileDecl --- cl/compile.go | 6 ++++-- lib/clang/clang.go | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cl/compile.go b/cl/compile.go index cce5ac61..d2c2ecc2 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -166,12 +166,14 @@ func compileFunc(ctx *blockCtx, fn clang.Cursor) { manglingName := c.GoString(m.CStr()) m.Dispose() - fnName := clang.String(fn) if _, ok := ctx.nameLookup(manglingName); !ok { - log.Println("func", fnName, "- skipped") + if debugCompileDecl { + log.Println("func", clang.String(fn), "- skipped") + } return } + fnName := clang.String(fn) if debugCompileDecl { log.Println("func", fnName, "-", clang.String(fn.Type())) } diff --git a/lib/clang/clang.go b/lib/clang/clang.go index 45667d7f..f37ed8a9 100644 --- a/lib/clang/clang.go +++ b/lib/clang/clang.go @@ -2569,8 +2569,7 @@ func VisitChildren( type Visitor func(cursor, parent Cursor, clientData ClientData) ChildVisitResult /** - * Visitor invoked for each file in a translation unit - * (used with clang_getInclusions()). + * Visitor invoked for each file in a translation unit (used with clang_getInclusions()). * * This visitor function will be invoked by clang_getInclusions() for each * file included (either at the top-level or by \#include directives) within @@ -2585,7 +2584,7 @@ type InclusionVisitor func(included_file File, inclusion_stack *SourceLocation, /** * Visit the set of preprocessor inclusions in a translation unit. * The visitor function is called with the provided data for every included - * file. This does not include headers included by the PCH file (unless one + * file. This does not include headers included by the PCH file (unless one * is inspecting the inclusions in the PCH file itself). */ //go:linkname GetInclusions C.clang_getInclusions From 4499b5998eebcce78f54bd5f83f98294815b747c Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 12 Sep 2026 22:20:27 +0800 Subject: [PATCH 6/8] cl: doc --- cl/cltest/cltest.go | 8 ++++---- cl/compile.go | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index e2c8fa64..cad9bfd9 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -25,8 +25,8 @@ import ( // ----------------------------------------------------------------------------- -// TestFromDir runs testFunc for each subdirectory of relDir. If sel is not empty, only subdirectories -// whose path contains sel will be tested. +// TestFromDir runs testFunc for each subdirectory of relDir. If sel is not empty, only +// subdirectories whose path contains sel will be tested. func TestFromDir(t *testing.T, sel, relDir string, testFunc func(t *testing.T, pkgDir string)) { dir, err := os.Getwd() if err != nil { @@ -54,8 +54,8 @@ func TestFromDir(t *testing.T, sel, relDir string, testFunc func(t *testing.T, p // ----------------------------------------------------------------------------- -// MockNameLookup is a mock implementation of the NameLookup function. It returns a fixed archive -// path and true for any input. +// MockNameLookup is a mock implementation of the NameLookup function. It returns a +// fixed archive path and true for any input. func MockNameLookup(manglingName string) (archivePath string, ok bool) { return "libfoo.a", true } diff --git a/cl/compile.go b/cl/compile.go index d2c2ecc2..b778116f 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -56,7 +56,8 @@ type Package struct { pi *PkgInfo } -// Reused specifies to reuse the Package instance between processing multiple C/C++ header files. +// Reused specifies to reuse the Package instance between processing multiple C/C++ +// header files. type Reused struct { pkg Package } @@ -75,11 +76,12 @@ type Config struct { // Include specifies include searching directories. Include []string - // Reused specifies to reuse the Package instance between processing multiple C/C++ header files. + // Reused specifies to reuse the Package instance between processing multiple C/C++ + // header files. *Reused - // NameLookup looks up the archive path for a given mangling name. It returns the archive - // path and a boolean indicating whether the lookup was successful. + // NameLookup looks up the archive path for a given mangling name. It returns the + // archive path and a boolean indicating whether the lookup was successful. NameLookup func(manglingName string) (archivePath string, ok bool) } From 7b365408af820dcd47a23544392af6ec85974fac Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 13 Sep 2026 04:09:15 +0800 Subject: [PATCH 7/8] cl compileFunc: manglingName starts with _ --- cl/compile.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cl/compile.go b/cl/compile.go index b778116f..38189366 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -22,6 +22,7 @@ import ( "go/types" "log" "strconv" + "strings" "github.com/goplus/gogen" "github.com/goplus/lib/c" @@ -167,6 +168,9 @@ func compileFunc(ctx *blockCtx, fn clang.Cursor) { m := fn.Mangling() manglingName := c.GoString(m.CStr()) m.Dispose() + if !strings.HasPrefix(manglingName, "_") { + manglingName = "_" + manglingName + } if _, ok := ctx.nameLookup(manglingName); !ok { if debugCompileDecl { From 6a76321ff45fbceb5d93810f905d273fdc402c24 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 13 Sep 2026 04:18:48 +0800 Subject: [PATCH 8/8] clang: Mangling --- cl/compile.go | 9 +-------- clang/mangling_addprefix.go | 37 +++++++++++++++++++++++++++++++++++++ clang/mangling_normal.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 clang/mangling_addprefix.go create mode 100644 clang/mangling_normal.go diff --git a/cl/compile.go b/cl/compile.go index 38189366..4bf179f0 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -22,7 +22,6 @@ import ( "go/types" "log" "strconv" - "strings" "github.com/goplus/gogen" "github.com/goplus/lib/c" @@ -165,13 +164,7 @@ func compileDecl(ctx *blockCtx, decl clang.Cursor) { // TODO(xsw): method support func compileFunc(ctx *blockCtx, fn clang.Cursor) { - m := fn.Mangling() - manglingName := c.GoString(m.CStr()) - m.Dispose() - if !strings.HasPrefix(manglingName, "_") { - manglingName = "_" + manglingName - } - + manglingName := clang.Mangling(fn) if _, ok := ctx.nameLookup(manglingName); !ok { if debugCompileDecl { log.Println("func", clang.String(fn), "- skipped") diff --git a/clang/mangling_addprefix.go b/clang/mangling_addprefix.go new file mode 100644 index 00000000..3cafcdd4 --- /dev/null +++ b/clang/mangling_addprefix.go @@ -0,0 +1,37 @@ +//go:build linux + +/* + * Copyright (c) 2026 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 clang + +import ( + "github.com/goplus/lib/c" +) + +// ----------------------------------------------------------------------------- + +/** + * Retrieve a name for the entity referenced by this cursor. + */ +func Mangling(fn Cursor) string { + m := fn.Mangling() + manglingName := c.GoString(m.CStr()) + m.Dispose() + return "_" + manglingName +} + +// ----------------------------------------------------------------------------- diff --git a/clang/mangling_normal.go b/clang/mangling_normal.go new file mode 100644 index 00000000..477de6d5 --- /dev/null +++ b/clang/mangling_normal.go @@ -0,0 +1,37 @@ +//go:build darwin || windows + +/* + * Copyright (c) 2026 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 clang + +import ( + "github.com/goplus/lib/c" +) + +// ----------------------------------------------------------------------------- + +/** + * Retrieve a name for the entity referenced by this cursor. + */ +func Mangling(fn Cursor) string { + m := fn.Mangling() + manglingName := c.GoString(m.CStr()) + m.Dispose() + return manglingName +} + +// -----------------------------------------------------------------------------