diff --git a/c/c.go b/c/c.go index 1a1aea8a..18f9dc1f 100644 --- a/c/c.go +++ b/c/c.go @@ -40,7 +40,7 @@ type integer interface { //go:linkname Str llgo.cstr func Str(string) *Char -//go:linkname Advance llgo.advance +// llgo:link Advance llgo.advance func Advance[PtrT any](ptr PtrT, offset int) PtrT { return ptr } // llgo:link Index llgo.index diff --git a/chore/gentests/gentests.go b/chore/gentests/gentests.go index f839dfc8..b57138a1 100644 --- a/chore/gentests/gentests.go +++ b/chore/gentests/gentests.go @@ -31,6 +31,7 @@ func main() { llgen.Verbose = false + llgenDir(dir + "/cl/_testlibc") llgenDir(dir + "/cl/_testrt") llgenDir(dir+"/cl/_testdata", "") } diff --git a/cl/_testdata/uint/out.ll b/cl/_testdata/uint/out.ll index ffcd184e..ceaaae3a 100644 --- a/cl/_testdata/uint/out.ll +++ b/cl/_testdata/uint/out.ll @@ -1,8 +1,6 @@ ; ModuleID = 'main' source_filename = "main" -%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } - @"main.init$guard" = global ptr null @__llgo_argc = global ptr null @__llgo_argv = global ptr null @@ -33,17 +31,11 @@ _llgo_0: store ptr %1, ptr @__llgo_argv, align 8 call void @"github.com/goplus/llgo/internal/runtime.init"() call void @main.init() - %2 = call %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr @0, i64 10) - %3 = call ptr @"github.com/goplus/llgo/c.Str"(%"github.com/goplus/llgo/internal/runtime.String" %2) - %4 = call i32 @main.f(i32 100) - %5 = call i32 (ptr, ...) @printf(ptr %3, i32 %4) + %2 = call i32 @main.f(i32 100) + %3 = call i32 (ptr, ...) @printf(ptr @0, i32 %2) ret void } declare void @"github.com/goplus/llgo/internal/runtime.init"() -declare ptr @"github.com/goplus/llgo/c.Str"(%"github.com/goplus/llgo/internal/runtime.String") - -declare %"github.com/goplus/llgo/internal/runtime.String" @"github.com/goplus/llgo/internal/runtime.NewString"(ptr, i64) - declare i32 @printf(ptr, ...) diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 460166e7..f3bbb844 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -165,7 +165,9 @@ func TestErrInitLinkname(t *testing.T) { } }() var ctx context - ctx.initLinkname("foo", "//go:linkname Printf printf", false) + ctx.initLinkname("foo", "//go:linkname Printf printf", func(name string) (isVar bool, ok bool) { + return false, name == "Printf" + }) } func TestErrVarOf(t *testing.T) { diff --git a/cl/import.go b/cl/import.go index da103c03..4951e793 100644 --- a/cl/import.go +++ b/cl/import.go @@ -29,19 +29,49 @@ import ( "golang.org/x/tools/go/ssa" ) -type contentLines = [][]byte -type contentMap = map[string]contentLines +type symInfo struct { + file string + isVar bool +} -func contentOf(m contentMap, file string) (lines contentLines, err error) { - if v, ok := m[file]; ok { - return v, nil +type pkgSymInfo struct { + files map[string][]byte // file => content + syms map[string]symInfo // name => isVar +} + +func newPkgSymInfo() *pkgSymInfo { + return &pkgSymInfo{ + files: make(map[string][]byte), + syms: make(map[string]symInfo), } - b, err := os.ReadFile(file) - if err == nil { - lines = bytes.Split(b, []byte{'\n'}) - m[file] = lines +} + +func (p *pkgSymInfo) addSym(fset *token.FileSet, pos token.Pos, name string, isVar bool) { + f := fset.File(pos) + if fp := f.Position(pos); fp.Line > 2 { + file := fp.Filename + if _, ok := p.files[file]; !ok { + b, err := os.ReadFile(file) + if err == nil { + p.files[file] = b + } + } + p.syms[name] = symInfo{file, isVar} + } +} + +func (p *pkgSymInfo) initLinknames(ctx *context, pkgPath string) { + for file, b := range p.files { + lines := bytes.Split(b, []byte{'\n'}) + for _, line := range lines { + ctx.initLinkname(pkgPath, string(line), func(name string) (isVar bool, ok bool) { + if sym, ok := p.syms[name]; ok && file == sym.file { + return sym.isVar, true + } + return + }) + } } - return } // PkgKindOf returns the kind of a package. @@ -87,23 +117,23 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) { i.kind = kind fset := p.fset names := scope.Names() - contents := make(contentMap) - pkgPath := llssa.PathOf(pkg) + syms := newPkgSymInfo() for _, name := range names { if token.IsExported(name) { obj := scope.Lookup(name) switch obj := obj.(type) { case *types.Func: if pos := obj.Pos(); pos != token.NoPos { - p.initLinknameByPos(fset, pos, pkgPath, contents, false) + syms.addSym(fset, pos, name, false) } case *types.Var: if pos := obj.Pos(); pos != token.NoPos { - p.initLinknameByPos(fset, pos, pkgPath, contents, true) + syms.addSym(fset, pos, name, true) } } } } + syms.initLinknames(p, llssa.PathOf(pkg)) } func (p *context) initFiles(pkgPath string, files []*ast.File) { @@ -112,64 +142,57 @@ func (p *context) initFiles(pkgPath string, files []*ast.File) { switch decl := decl.(type) { case *ast.FuncDecl: if decl.Recv == nil { - p.initLinknameByDoc(decl.Doc, pkgPath, false) + p.initLinknameByDoc(decl.Doc, pkgPath, decl.Name.Name, false) } case *ast.GenDecl: if decl.Tok == token.VAR && len(decl.Specs) == 1 { - p.initLinknameByDoc(decl.Doc, pkgPath, true) + if names := decl.Specs[0].(*ast.ValueSpec).Names; len(names) == 1 { + p.initLinknameByDoc(decl.Doc, pkgPath, names[0].Name, true) + } } } } } } -func (p *context) initLinknameByDoc(doc *ast.CommentGroup, pkgPath string, isVar bool) { +func (p *context) initLinknameByDoc(doc *ast.CommentGroup, pkgPath, expName string, isVar bool) { if doc != nil { if n := len(doc.List); n > 0 { line := doc.List[n-1].Text - p.initLinkname(pkgPath, line, isVar) + p.initLinkname(pkgPath, line, func(name string) (bool, bool) { + return isVar, name == expName + }) } } } -func (p *context) initLinknameByPos(fset *token.FileSet, pos token.Pos, pkgPath string, contents contentMap, isVar bool) { - f := fset.File(pos) - if fp := f.Position(pos); fp.Line > 2 { - lines, err := contentOf(contents, fp.Filename) - if err != nil { - panic(err) - } - if i := fp.Line - 2; i < len(lines) { - line := string(lines[i]) - p.initLinkname(pkgPath, line, isVar) - } - } -} - -func (p *context) initLinkname(pkgPath, line string, isVar bool) { +func (p *context) initLinkname(pkgPath, line string, f func(name string) (isVar bool, ok bool)) { const ( linkname = "//go:linkname " llgolink = "//llgo:link " llgolink2 = "// llgo:link " ) if strings.HasPrefix(line, linkname) { - p.initLink(pkgPath, line, len(linkname), isVar) + p.initLink(pkgPath, line, len(linkname), f) } else if strings.HasPrefix(line, llgolink2) { - p.initLink(pkgPath, line, len(llgolink2), isVar) + p.initLink(pkgPath, line, len(llgolink2), f) } else if strings.HasPrefix(line, llgolink) { - p.initLink(pkgPath, line, len(llgolink), isVar) + p.initLink(pkgPath, line, len(llgolink), f) } } -func (p *context) initLink(pkgPath string, line string, prefix int, isVar bool) { +func (p *context) initLink(pkgPath string, line string, prefix int, f func(name string) (isVar bool, ok bool)) { text := strings.TrimSpace(line[prefix:]) if idx := strings.IndexByte(text, ' '); idx > 0 { - link := strings.TrimLeft(text[idx+1:], " ") - if isVar || strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr - name := pkgPath + "." + text[:idx] - p.link[name] = link - } else { - panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf") + name := text[:idx] + if isVar, ok := f(name); ok { + link := strings.TrimLeft(text[idx+1:], " ") + if isVar || strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr + name := pkgPath + "." + name + p.link[name] = link + } else { + panic(line + ": no specified call convention. eg. //go:linkname Printf C.printf") + } } } }