diff --git a/cl/_testdata/apkg/in.go b/cl/_testdata/apkg/in.go new file mode 100644 index 00000000..c5416fad --- /dev/null +++ b/cl/_testdata/apkg/in.go @@ -0,0 +1,8 @@ +package apkg + +func Max(a, b float64) float64 { + if a > b { + return a + } + return b +} diff --git a/cl/_testdata/apkg/out.ll b/cl/_testdata/apkg/out.ll new file mode 100644 index 00000000..75094713 --- /dev/null +++ b/cl/_testdata/apkg/out.ll @@ -0,0 +1,29 @@ +; ModuleID = 'apkg' +source_filename = "apkg" + +@"init$guard" = global ptr null + +define void @apkg.init() { +_llgo_0: + %0 = load i1, ptr @"init$guard", align 1 + br i1 %0, label %_llgo_2, label %_llgo_1 + +_llgo_1: ; preds = %_llgo_0 + store i1 true, ptr @"init$guard", align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define double @apkg.Max(double %0, double %1) { +_llgo_0: + %2 = fcmp ogt double %0, %1 + br i1 %2, label %_llgo_1, label %_llgo_2 + +_llgo_1: ; preds = %_llgo_0 + ret double %0 + +_llgo_2: ; preds = %_llgo_0 + ret double %1 +} diff --git a/cl/_testdata/fncall/out.ll b/cl/_testdata/fncall/out.ll index 56abe836..223503e0 100644 --- a/cl/_testdata/fncall/out.ll +++ b/cl/_testdata/fncall/out.ll @@ -3,7 +3,7 @@ source_filename = "main" @"init$guard" = global ptr null -define void @init() { +define void @main.init() { _llgo_0: %0 = load i1, ptr @"init$guard", align 1 br i1 %0, label %_llgo_2, label %_llgo_1 @@ -16,7 +16,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } -define i64 @max(i64 %0, i64 %1) { +define i64 @main.max(i64 %0, i64 %1) { _llgo_0: %2 = icmp sgt i64 %0, %1 br i1 %2, label %_llgo_1, label %_llgo_2 @@ -30,7 +30,7 @@ _llgo_2: ; preds = %_llgo_0 define void @main() { _llgo_0: - call void @init() - %0 = call i64 @max(i64 1, i64 2) + call void @main.init() + %0 = call i64 @main.max(i64 1, i64 2) ret void } diff --git a/cl/_testdata/printf/in.go b/cl/_testdata/printf/in.go index 00b867e8..8d7d1264 100644 --- a/cl/_testdata/printf/in.go +++ b/cl/_testdata/printf/in.go @@ -2,7 +2,7 @@ package main import _ "unsafe" -//go:linkname printf _printf +//go:linkname printf printf func printf(format *int8, __llgo_va_list ...any) var hello = [...]int8{'H', 'e', 'l', 'l', 'o', '\n', 0} diff --git a/cl/_testdata/printf/out.ll b/cl/_testdata/printf/out.ll index 98881961..6f6cc558 100644 --- a/cl/_testdata/printf/out.ll +++ b/cl/_testdata/printf/out.ll @@ -4,7 +4,7 @@ source_filename = "main" @"init$guard" = global ptr null @hello = global ptr null -define void @init() { +define void @main.init() { _llgo_0: %0 = load i1, ptr @"init$guard", align 1 br i1 %0, label %_llgo_2, label %_llgo_1 @@ -28,7 +28,7 @@ declare void @printf(ptr, ...) define void @main() { _llgo_0: - call void @init() + call void @main.init() call void (ptr, ...) @printf(ptr @hello) ret void } diff --git a/cl/_testdata/varinit/out.ll b/cl/_testdata/varinit/out.ll index 5a045a52..a8a4848b 100644 --- a/cl/_testdata/varinit/out.ll +++ b/cl/_testdata/varinit/out.ll @@ -4,7 +4,7 @@ source_filename = "main" @"init$guard" = global ptr null @a = global ptr null -define void @init() { +define void @main.init() { _llgo_0: %0 = load i1, ptr @"init$guard", align 1 br i1 %0, label %_llgo_2, label %_llgo_1 @@ -20,7 +20,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 define void @main() { _llgo_0: - call void @init() + call void @main.init() %0 = load i64, ptr @a, align 4 %1 = add i64 %0, 1 store i64 %1, ptr @a, align 4 diff --git a/cl/compile.go b/cl/compile.go index 7974d9a3..f3e761ae 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -18,9 +18,11 @@ package cl import ( "fmt" + "go/ast" "log" "os" "sort" + "strings" llssa "github.com/goplus/llgo/ssa" "golang.org/x/tools/go/ssa" @@ -73,8 +75,12 @@ func funcKind(vfn ssa.Value) int { return fnNormal } -func isMainFunc(fn *ssa.Function) bool { - return fn.Name() == "main" && fn.Pkg.Pkg.Path() == "main" +func funcName(fn *ssa.Function) string { + ret := fn.Pkg.Pkg.Path() + "." + fn.Name() + if ret == "main.main" { + ret = "main" + } + return ret } // ----------------------------------------------------------------------------- @@ -89,10 +95,46 @@ type context struct { pkg llssa.Package fn llssa.Function goPkg *ssa.Package + link map[string]string bvals map[ssa.Value]llssa.Expr // block values inits []func() } +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 { + if decl.Recv == nil && decl.Doc != nil { + p.initLinkname(pkgPath, decl.Doc) + } + } + } + } +} + +func (p *context) initLinkname(pkgPath string, doc *ast.CommentGroup) { + const ( + linkname = "//go:linkname " + ) + for _, c := range doc.List { + if strings.HasPrefix(c.Text, linkname) { + text := strings.TrimSpace(c.Text[len(linkname):]) + if idx := strings.IndexByte(text, ' '); idx > 0 { + name := pkgPath + "." + text[:idx] + p.link[name] = strings.TrimLeft(text[idx+1:], " ") + } + } + } +} + +func (p *context) funcName(fn *ssa.Function) string { + name := funcName(fn) + if v, ok := p.link[name]; ok { + return v + } + return name +} + func (p *context) compileType(pkg llssa.Package, member *ssa.Type) { panic("todo") } @@ -108,7 +150,7 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { } func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) { - name := f.Name() + name := p.funcName(f) if debugInstr { log.Println("==> NewFunc", name) } @@ -130,9 +172,8 @@ func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) { } fn.MakeBlocks(nblk) b := fn.NewBuilder() - isMain := isMainFunc(f) for i, block := range f.Blocks { - p.compileBlock(b, block, isMain && i == 0) + p.compileBlock(b, block, i == 0 && name == "main") } }) } @@ -142,7 +183,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, doInit bo b.SetBlock(ret) p.bvals = make(map[ssa.Value]llssa.Expr) if doInit { - fn := p.pkg.FuncOf("init") + fn := p.pkg.FuncOf("main.init") b.Call(fn.Expr) } for _, instr := range block.Instrs { @@ -241,7 +282,7 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr { if v.Pkg != p.goPkg { panic("todo") } - fn := p.pkg.FuncOf(v.Name()) + fn := p.pkg.FuncOf(p.funcName(v)) return fn.Expr case *ssa.Global: if v.Pkg != p.goPkg { @@ -281,16 +322,20 @@ func (p *context) compileValues(b llssa.Builder, vals []ssa.Value, hasVArg int) // ----------------------------------------------------------------------------- -type Config struct { +type GoPackage struct { + SSA *ssa.Package + Files []*ast.File } // NewPackage compiles a Go package to LLVM IR package. -func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.Package, err error) { +func NewPackage(prog llssa.Program, in *GoPackage) (ret llssa.Package, err error) { type namedMember struct { name string val ssa.Member } + pkg := in.SSA + // Sort by position, so that the order of the functions in the IR matches // the order of functions in the source file. This is useful for testing, // for example. @@ -311,7 +356,9 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, conf *Config) (ret llssa.P prog: prog, pkg: ret, goPkg: pkg, + link: make(map[string]string), } + ctx.initFiles(pkgTypes.Path(), in.Files) for _, m := range members { member := m.val switch member := member.(type) { diff --git a/cl/compile_test.go b/cl/compile_test.go index aac37468..30a2a6b5 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -95,7 +95,7 @@ func testCompileEx(t *testing.T, src any, fname, expected string) { } foo.WriteTo(os.Stderr) prog := llssa.NewProgram(nil) - ret, err := NewPackage(prog, foo, nil) + ret, err := NewPackage(prog, &GoPackage{SSA: foo, Files: files}) if err != nil { t.Fatal("cl.NewPackage failed:", err) } @@ -119,7 +119,7 @@ source_filename = "foo" @"init$guard" = global ptr null @a = global ptr null -define void @init() { +define void @foo.init() { _llgo_0: %0 = load i1, ptr @"init$guard", align 1 br i1 %0, label %_llgo_2, label %_llgo_1 @@ -145,7 +145,7 @@ source_filename = "foo" @"init$guard" = global ptr null -define void @init() { +define void @foo.init() { _llgo_0: %0 = load i1, ptr @"init$guard", align 1 br i1 %0, label %_llgo_2, label %_llgo_1 @@ -158,7 +158,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } -define i64 @fn(i64 %0, double %1) { +define i64 @foo.fn(i64 %0, double %1) { _llgo_0: ret i64 1 } diff --git a/cl/internal/stdio/printf.go b/cl/internal/stdio/printf.go new file mode 100644 index 00000000..9887fa93 --- /dev/null +++ b/cl/internal/stdio/printf.go @@ -0,0 +1,6 @@ +package stdio + +import _ "unsafe" + +//go:linkname printf printf +func printf(format *int8, __llgo_va_list ...any) diff --git a/internal/llgen/llgen.go b/internal/llgen/llgen.go index ae89e26a..4f91aa4e 100644 --- a/internal/llgen/llgen.go +++ b/internal/llgen/llgen.go @@ -58,7 +58,7 @@ func Gen(inFile string, src any) string { ssaPkg.WriteTo(os.Stderr) prog := llssa.NewProgram(nil) - ret, err := cl.NewPackage(prog, ssaPkg, nil) + ret, err := cl.NewPackage(prog, &cl.GoPackage{SSA: ssaPkg, Files: files}) check(err) return ret.String() diff --git a/ssa/expr.go b/ssa/expr.go index 4787e404..970abbc6 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -230,7 +230,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr { return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret} case vkFloat: pred := floatPredOpToLLVM[op-predOpBase] - return Expr{llvm.ConstFCmp(pred, x.impl, y.impl), tret} + return Expr{llvm.CreateFCmp(b.impl, pred, x.impl, y.impl), tret} case vkString, vkComplex, vkBool: panic("todo") }