From afd02b3d7827915892ca4735199e8aeff045922f Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 1 May 2024 20:05:28 +0800 Subject: [PATCH 1/4] cl: to support stdin/stdout/stderr --- cl/_testrt/fprintf/in.go | 9 +++++++++ cl/_testrt/fprintf/out.ll | 32 ++++++++++++++++++++++++++++++++ cl/compile_test.go | 2 +- internal/runtime/c/c.go | 13 +++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 cl/_testrt/fprintf/in.go create mode 100644 cl/_testrt/fprintf/out.ll diff --git a/cl/_testrt/fprintf/in.go b/cl/_testrt/fprintf/in.go new file mode 100644 index 00000000..5328026a --- /dev/null +++ b/cl/_testrt/fprintf/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/fprintf/out.ll b/cl/_testrt/fprintf/out.ll new file mode 100644 index 00000000..6c2c224c --- /dev/null +++ b/cl/_testrt/fprintf/out.ll @@ -0,0 +1,32 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@"github.com/goplus/llgo/internal/runtime/c.Stderr" = 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 @"github.com/goplus/llgo/internal/runtime/c.Stderr", 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/compile_test.go b/cl/compile_test.go index b9c3443f..de7abd2a 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -29,7 +29,7 @@ func testCompile(t *testing.T, src, expected string) { } func TestFromTestrt(t *testing.T) { - cltest.FromDir(t, "", "./_testrt", true) + cltest.FromDir(t, "fprintf", "./_testrt", true) } func TestFromTestdata(t *testing.T) { 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 From 8f15fd45f2cbe0218cc88dcf10bf4b75306f8569 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 1 May 2024 20:33:31 +0800 Subject: [PATCH 2/4] initLinkname: support var --- cl/builtin_test.go | 2 +- cl/import.go | 39 ++++++++++++++++++++++++--------------- 2 files changed, 25 insertions(+), 16 deletions(-) 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/import.go b/cl/import.go index a2a380f7..fbb06e49 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) } } } @@ -117,7 +112,7 @@ func (p *context) initFiles(pkgPath string, files []*ast.File) { if doc := decl.Doc; doc != nil { if n := len(doc.List); n > 0 { line := doc.List[n-1].Text - p.initLinkname(pkgPath, line) + p.initLinkname(pkgPath, line, false) } } } @@ -126,7 +121,21 @@ func (p *context) initFiles(pkgPath string, files []*ast.File) { } } -func (p *context) initLinkname(pkgPath, line string) { +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 +143,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 { From 8d3cb246c2048bef61565aa06e63c2fcdab306b3 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 1 May 2024 20:56:51 +0800 Subject: [PATCH 3/4] cl: linkname of varName --- cl/_testrt/fprintf/out.ll | 4 ++-- cl/compile.go | 2 +- cl/compile_test.go | 2 +- cl/import.go | 18 +++++++++++++----- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/cl/_testrt/fprintf/out.ll b/cl/_testrt/fprintf/out.ll index 6c2c224c..ee0cd9d7 100644 --- a/cl/_testrt/fprintf/out.ll +++ b/cl/_testrt/fprintf/out.ll @@ -2,7 +2,7 @@ source_filename = "main" @"main.init$guard" = global ptr null -@"github.com/goplus/llgo/internal/runtime/c.Stderr" = external global ptr +@__stderrp = external global ptr @0 = private unnamed_addr constant [10 x i8] c"Hello %d\0A\00", align 1 define void @main.init() { @@ -22,7 +22,7 @@ define void @main() { _llgo_0: call void @"github.com/goplus/llgo/internal/runtime.init"() call void @main.init() - %0 = load ptr, ptr @"github.com/goplus/llgo/internal/runtime/c.Stderr", align 8 + %0 = load ptr, ptr @__stderrp, align 8 %1 = call i32 (ptr, ptr, ...) @fprintf(ptr %0, ptr @0, i64 100) ret void } diff --git a/cl/compile.go b/cl/compile.go index c70c59c6..a8975ed7 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 := p.varName(gbl.Pkg.Pkg, gbl) if ignoreName(name) || checkCgo(gbl.Name()) { return } diff --git a/cl/compile_test.go b/cl/compile_test.go index de7abd2a..b9c3443f 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -29,7 +29,7 @@ func testCompile(t *testing.T, src, expected string) { } func TestFromTestrt(t *testing.T) { - cltest.FromDir(t, "fprintf", "./_testrt", true) + cltest.FromDir(t, "", "./_testrt", true) } func TestFromTestdata(t *testing.T) { diff --git a/cl/import.go b/cl/import.go index fbb06e49..0404deb5 100644 --- a/cl/import.go +++ b/cl/import.go @@ -210,6 +210,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) string { + name := llssa.FullName(pkg, v.Name()) + if v, ok := p.link[name]; ok { + return v + } + return name +} + // 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) { @@ -235,14 +243,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 { From 52a64a7770d9a7524e3080bda11cbd5cfe07fe9e Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 1 May 2024 21:18:28 +0800 Subject: [PATCH 4/4] cl: initLinknameByDoc --- cl/_testrt/concat/in.go | 9 +++++++++ cl/_testrt/concat/out.ll | 32 ++++++++++++++++++++++++++++++++ cl/_testrt/fprintf/in.go | 15 +++++++++++---- cl/_testrt/fprintf/out.ll | 6 +++--- cl/compile.go | 6 ++++-- cl/import.go | 31 ++++++++++++++++++++----------- 6 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 cl/_testrt/concat/in.go create mode 100644 cl/_testrt/concat/out.ll 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 index 5328026a..b6821913 100644 --- a/cl/_testrt/fprintf/in.go +++ b/cl/_testrt/fprintf/in.go @@ -1,9 +1,16 @@ package main -import ( - "github.com/goplus/llgo/internal/runtime/c" -) +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() { - c.Fprintf(c.Stderr, c.Str("Hello %d\n"), 100) + fprintf(stderr, cstr("Hello %d\n"), 100) } diff --git a/cl/_testrt/fprintf/out.ll b/cl/_testrt/fprintf/out.ll index ee0cd9d7..03f4adf7 100644 --- a/cl/_testrt/fprintf/out.ll +++ b/cl/_testrt/fprintf/out.ll @@ -5,6 +5,8 @@ source_filename = "main" @__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 @@ -23,10 +25,8 @@ _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) + call void (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/compile.go b/cl/compile.go index a8975ed7..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 := p.varName(gbl.Pkg.Pkg, gbl) + 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 0404deb5..bdfa1050 100644 --- a/cl/import.go +++ b/cl/import.go @@ -107,20 +107,29 @@ 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, false) - } - } + 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) 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 { @@ -210,12 +219,12 @@ 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) string { +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 + return v, false } - return name + return name, true } // funcOf returns a function by name and set ftype = goFunc, cFunc, etc. @@ -246,7 +255,7 @@ func (p *context) funcOf(fn *ssa.Function) (ret llssa.Function, ftype int) { func (p *context) varOf(v *ssa.Global) (ret llssa.Global) { pkgTypes := p.ensureLoaded(v.Pkg.Pkg) pkg := p.pkg - name := p.varName(pkgTypes, v) + name, _ := p.varName(pkgTypes, v) if ret = pkg.VarOf(name); ret == nil { ret = pkg.NewVar(name, v.Type()) }