diff --git a/cl/_testrt/concat/in.go b/cl/_testrt/concat/in.go new file mode 100644 index 00000000..5328026a --- /dev/null +++ b/cl/_testrt/concat/in.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/goplus/llgo/internal/runtime/c" +) + +func main() { + c.Fprintf(c.Stderr, c.Str("Hello %d\n"), 100) +} diff --git a/cl/_testrt/concat/out.ll b/cl/_testrt/concat/out.ll new file mode 100644 index 00000000..ee0cd9d7 --- /dev/null +++ b/cl/_testrt/concat/out.ll @@ -0,0 +1,32 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@__stderrp = external global ptr +@0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1 + +define void @main.init() { +_llgo_0: + %0 = load i1, ptr @"main.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"main.init$guard", align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main() { +_llgo_0: + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %0 = load ptr, ptr @__stderrp, align 8 + %1 = call i32 (ptr, ptr, ...) @fprintf(ptr %0, ptr @0, i64 100) + ret void +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare i32 @fprintf(ptr, ptr, ...) diff --git a/cl/_testrt/fprintf/in.go b/cl/_testrt/fprintf/in.go new file mode 100644 index 00000000..b6821913 --- /dev/null +++ b/cl/_testrt/fprintf/in.go @@ -0,0 +1,16 @@ +package main + +import "unsafe" + +//go:linkname cstr llgo.cstr +func cstr(string) *int8 + +//go:linkname stderr __stderrp +var stderr unsafe.Pointer + +//go:linkname fprintf C.fprintf +func fprintf(fp unsafe.Pointer, format *int8, __llgo_va_list ...any) + +func main() { + fprintf(stderr, cstr("Hello %d\n"), 100) +} diff --git a/cl/_testrt/fprintf/out.ll b/cl/_testrt/fprintf/out.ll new file mode 100644 index 00000000..03f4adf7 --- /dev/null +++ b/cl/_testrt/fprintf/out.ll @@ -0,0 +1,32 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@__stderrp = external global ptr +@0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1 + +declare void @fprintf(ptr, ptr, ...) + +define void @main.init() { +_llgo_0: + %0 = load i1, ptr @"main.init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"main.init$guard", align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main() { +_llgo_0: + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %0 = load ptr, ptr @__stderrp, align 8 + call void (ptr, ptr, ...) @fprintf(ptr %0, ptr @0, i64 100) + ret void +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() diff --git a/cl/builtin_test.go b/cl/builtin_test.go index a3fa722a..66d0e839 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -122,7 +122,7 @@ func TestErrInitLinkname(t *testing.T) { } }() var ctx context - ctx.initLinkname("foo", "//go:linkname Printf printf") + ctx.initLinkname("foo", "//go:linkname Printf printf", false) } func TestErrVarOf(t *testing.T) { diff --git a/cl/compile.go b/cl/compile.go index c70c59c6..c374a5d5 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -177,7 +177,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) { // Global variable. func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { typ := gbl.Type() - name := llssa.FullName(gbl.Pkg.Pkg, gbl.Name()) + name, isDef := p.varName(gbl.Pkg.Pkg, gbl) if ignoreName(name) || checkCgo(gbl.Name()) { return } @@ -185,7 +185,9 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { log.Println("==> NewVar", name, typ) } g := pkg.NewVar(name, typ) - g.Init(p.prog.Null(g.Type)) + if isDef { + g.Init(p.prog.Null(g.Type)) + } } func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) { diff --git a/cl/import.go b/cl/import.go index a2a380f7..bdfa1050 100644 --- a/cl/import.go +++ b/cl/import.go @@ -90,19 +90,14 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) { for _, name := range names { if token.IsExported(name) { obj := scope.Lookup(name) - if obj, ok := obj.(*types.Func); ok { + switch obj := obj.(type) { + case *types.Func: if pos := obj.Pos(); pos != token.NoPos { - 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) - } - } + p.initLinknameByPos(fset, pos, pkgPath, contents, false) + } + case *types.Var: + if pos := obj.Pos(); pos != token.NoPos { + p.initLinknameByPos(fset, pos, pkgPath, contents, true) } } } @@ -112,21 +107,44 @@ func (p *context) importPkg(pkg *types.Package, i *pkgInfo) { func (p *context) initFiles(pkgPath string, files []*ast.File) { for _, file := range files { for _, decl := range file.Decls { - if decl, ok := decl.(*ast.FuncDecl); ok { + switch decl := decl.(type) { + case *ast.FuncDecl: if decl.Recv == nil { - if doc := decl.Doc; doc != nil { - if n := len(doc.List); n > 0 { - line := doc.List[n-1].Text - p.initLinkname(pkgPath, line) - } - } + p.initLinknameByDoc(decl.Doc, pkgPath, false) + } + case *ast.GenDecl: + if decl.Tok == token.VAR && len(decl.Specs) == 1 { + p.initLinknameByDoc(decl.Doc, pkgPath, true) } } } } } -func (p *context) initLinkname(pkgPath, line string) { +func (p *context) initLinknameByDoc(doc *ast.CommentGroup, pkgPath string, isVar bool) { + if doc != nil { + if n := len(doc.List); n > 0 { + line := doc.List[n-1].Text + p.initLinkname(pkgPath, line, isVar) + } + } +} + +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) { const ( linkname = "//go:linkname " ) @@ -134,7 +152,7 @@ func (p *context) initLinkname(pkgPath, line string) { text := strings.TrimSpace(line[len(linkname):]) if idx := strings.IndexByte(text, ' '); idx > 0 { link := strings.TrimLeft(text[idx+1:], " ") - if strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr + if isVar || strings.Contains(link, ".") { // eg. C.printf, C.strlen, llgo.cstr name := pkgPath + "." + text[:idx] p.link[name] = link } else { @@ -201,6 +219,14 @@ func (p *context) funcName(pkg *types.Package, fn *ssa.Function, ignore bool) (s return name, goFunc } +func (p *context) varName(pkg *types.Package, v *ssa.Global) (vName string, isDef bool) { + name := llssa.FullName(pkg, v.Name()) + if v, ok := p.link[name]; ok { + return v, false + } + return name, true +} + // 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) (ret llssa.Function, ftype int) { @@ -226,14 +252,14 @@ func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) { return } -func (p *context) varOf(v *ssa.Global) llssa.Global { +func (p *context) varOf(v *ssa.Global) (ret llssa.Global) { pkgTypes := p.ensureLoaded(v.Pkg.Pkg) pkg := p.pkg - name := llssa.FullName(pkgTypes, v.Name()) - if ret := pkg.VarOf(name); ret != nil { - return ret + name, _ := p.varName(pkgTypes, v) + if ret = pkg.VarOf(name); ret == nil { + ret = pkg.NewVar(name, v.Type()) } - return pkg.NewVar(name, v.Type()) + return } func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package { diff --git a/internal/runtime/c/c.go b/internal/runtime/c/c.go index 79763a1b..05bd8ec6 100644 --- a/internal/runtime/c/c.go +++ b/internal/runtime/c/c.go @@ -27,8 +27,18 @@ type ( Char = int8 Int = C.int Pointer = unsafe.Pointer + FilePtr = unsafe.Pointer ) +//go:linkname Stdin __stdinp +var Stdin FilePtr + +//go:linkname Stdout __stdoutp +var Stdout FilePtr + +//go:linkname Stderr __stderrp +var Stderr FilePtr + //go:linkname Str llgo.cstr func Str(string) *Char @@ -52,3 +62,6 @@ func Memcpy(dst, src Pointer, n uintptr) Pointer //go:linkname Printf C.printf func Printf(format *Char, __llgo_va_list ...any) Int + +//go:linkname Fprintf C.fprintf +func Fprintf(fp FilePtr, format *Char, __llgo_va_list ...any) Int