diff --git a/chore/llgen/llgen.go b/chore/llgen/llgen.go index 594076da..ca7ce200 100644 --- a/chore/llgen/llgen.go +++ b/chore/llgen/llgen.go @@ -35,13 +35,10 @@ func main() { dir, _ := filepath.Split(inFile) outFile := dir + "out.ll" - pkgPath := "" - if len(os.Args) == 3 { - pkgPath = os.Args[2] - } else { - pkgPath = llgen.PkgPath(dir) - } - llgen.Init() - llgen.Do(pkgPath, inFile, outFile) + if len(os.Args) >= 3 { + llgen.Do(os.Args[2], inFile, outFile) + } else { + llgen.DoFile(inFile, outFile) + } } diff --git a/cl/_testcgo/strlen/in.go b/cl/_testcgo/strlen/in.go new file mode 100644 index 00000000..edaa5e9d --- /dev/null +++ b/cl/_testcgo/strlen/in.go @@ -0,0 +1,17 @@ +package main + +import "C" +import _ "unsafe" + +//go:linkname printf printf +func printf(format *int8, __llgo_va_list ...any) + +//go:linkname strlen strlen +func strlen(str *int8) C.int + +var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0} + +func main() { + sfmt := &format[0] + printf(sfmt, strlen(sfmt)) +} diff --git a/cl/_testcgo/strlen/out.ll b/cl/_testcgo/strlen/out.ll new file mode 100644 index 00000000..706fadc8 --- /dev/null +++ b/cl/_testcgo/strlen/out.ll @@ -0,0 +1,41 @@ +; ModuleID = 'main' +source_filename = "main" + +@main._Cgo_always_false = global ptr null +@main.format = global ptr null +@"main.init$guard" = global ptr null + +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 + store i8 72, ptr @main.format, align 1 + store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1 + store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1 + store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1 + store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1 + store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1 + store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1 + store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1 + store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1 + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main() { +_llgo_0: + call void @main.init() + %0 = call i32 @strlen(ptr @main.format) + call void (ptr, ...) @printf(ptr @main.format, i32 %0) + ret void +} + +declare void @printf(ptr, ...) + +declare i32 @strlen(ptr) diff --git a/cl/_testdata/apkg/out.ll b/cl/_testdata/apkg/out.ll index 7959439a..3e1aab2d 100644 --- a/cl/_testdata/apkg/out.ll +++ b/cl/_testdata/apkg/out.ll @@ -3,6 +3,18 @@ source_filename = "apkg" @"apkg.init$guard" = global ptr null +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 +} + define void @apkg.init() { _llgo_0: %0 = load i1, ptr @"apkg.init$guard", align 1 @@ -15,15 +27,3 @@ _llgo_1: ; preds = %_llgo_0 _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 b0325800..4adec91a 100644 --- a/cl/_testdata/fncall/out.ll +++ b/cl/_testdata/fncall/out.ll @@ -16,6 +16,13 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } +define void @main() { +_llgo_0: + call void @main.init() + %0 = call i64 @main.max(i64 1, i64 2) + ret void +} + define i64 @main.max(i64 %0, i64 %1) { _llgo_0: %2 = icmp sgt i64 %0, %1 @@ -27,10 +34,3 @@ _llgo_1: ; preds = %_llgo_0 _llgo_2: ; preds = %_llgo_0 ret i64 %1 } - -define void @main() { -_llgo_0: - call void @main.init() - %0 = call i64 @main.max(i64 1, i64 2) - ret void -} diff --git a/cl/_testdata/importpkg/out.ll b/cl/_testdata/importpkg/out.ll index de2212b5..8aa4cbb1 100644 --- a/cl/_testdata/importpkg/out.ll +++ b/cl/_testdata/importpkg/out.ll @@ -1,8 +1,8 @@ ; ModuleID = 'main' source_filename = "main" -@"main.init$guard" = global ptr null @main.hello = global ptr null +@"main.init$guard" = global ptr null define void @main.init() { _llgo_0: diff --git a/cl/_testdata/method/out.ll b/cl/_testdata/method/out.ll index d5df9805..8d3f9f96 100644 --- a/cl/_testdata/method/out.ll +++ b/cl/_testdata/method/out.ll @@ -1,8 +1,21 @@ ; ModuleID = 'main' source_filename = "main" -@"main.init$guard" = global ptr null @main.format = global ptr null +@"main.init$guard" = global ptr null + +define i64 @"(T).Add"(i64 %0, i64 %1) { +_llgo_0: + %2 = add i64 %0, %1 + ret i64 %2 +} + +define i64 @"(*T).Add"(ptr %0, i64 %1) { +_llgo_0: + %2 = load i64, ptr %0, align 4 + %3 = call i64 @"(T).Add"(i64 %2, i64 %1) + ret i64 %3 +} define void @main.init() { _llgo_0: @@ -27,25 +40,12 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } -define i64 @"(main.T).Add"(i64 %0, i64 %1) { -_llgo_0: - %2 = add i64 %0, %1 - ret i64 %2 -} - -define i64 @"(*main.T).Add"(ptr %0, i64 %1) { -_llgo_0: - %2 = load i64, ptr %0, align 4 - %3 = call i64 @"(main.T).Add"(i64 %2, i64 %1) - ret i64 %3 -} - -declare void @printf(ptr, ...) - define void @main() { _llgo_0: call void @main.init() - %0 = call i64 @"(main.T).Add"(i64 1, i64 2) + %0 = call i64 @"(T).Add"(i64 1, i64 2) call void (ptr, ...) @printf(ptr @main.format, i64 %0) ret void } + +declare void @printf(ptr, ...) diff --git a/cl/_testdata/printf/out.ll b/cl/_testdata/printf/out.ll index a5532ec5..65ac64ae 100644 --- a/cl/_testdata/printf/out.ll +++ b/cl/_testdata/printf/out.ll @@ -1,8 +1,8 @@ ; ModuleID = 'main' source_filename = "main" -@"main.init$guard" = global ptr null @main.hello = global ptr null +@"main.init$guard" = global ptr null define void @main.init() { _llgo_0: @@ -24,11 +24,11 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } -declare void @printf(ptr, ...) - define void @main() { _llgo_0: call void @main.init() call void (ptr, ...) @printf(ptr @main.hello) ret void } + +declare void @printf(ptr, ...) diff --git a/cl/_testdata/printval/out.ll b/cl/_testdata/printval/out.ll index 62e449a3..e7fe3bd9 100644 --- a/cl/_testdata/printval/out.ll +++ b/cl/_testdata/printval/out.ll @@ -1,8 +1,8 @@ ; ModuleID = 'main' source_filename = "main" -@"main.init$guard" = global ptr null @main.format = global ptr null +@"main.init$guard" = global ptr null define void @main.init() { _llgo_0: @@ -27,11 +27,11 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } -declare void @printf(ptr, ...) - define void @main() { _llgo_0: call void @main.init() call void (ptr, ...) @printf(ptr @main.format, i64 100) ret void } + +declare void @printf(ptr, ...) diff --git a/cl/_testdata/ptrmthd/out.ll b/cl/_testdata/ptrmthd/out.ll index 32cd1d0e..77665e9d 100644 --- a/cl/_testdata/ptrmthd/out.ll +++ b/cl/_testdata/ptrmthd/out.ll @@ -1,8 +1,14 @@ ; ModuleID = 'main' source_filename = "main" -@"main.init$guard" = global ptr null @main.format = global ptr null +@"main.init$guard" = global ptr null + +define void @"(*T).Print"(ptr %0, i64 %1) { +_llgo_0: + call void (ptr, ...) @printf(ptr %0, i64 %1) + ret void +} define void @main.init() { _llgo_0: @@ -27,17 +33,11 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } -declare void @printf(ptr, ...) - -define void @"(*main.T).Print"(ptr %0, i64 %1) { -_llgo_0: - call void (ptr, ...) @printf(ptr %0, i64 %1) - ret void -} - define void @main() { _llgo_0: call void @main.init() - call void @"(*main.T).Print"(ptr @main.format, i64 100) + call void @"(*T).Print"(ptr @main.format, i64 100) ret void } + +declare void @printf(ptr, ...) diff --git a/cl/_testdata/varinit/out.ll b/cl/_testdata/varinit/out.ll index 3052516c..9aebe715 100644 --- a/cl/_testdata/varinit/out.ll +++ b/cl/_testdata/varinit/out.ll @@ -1,8 +1,8 @@ ; ModuleID = 'main' source_filename = "main" -@"main.init$guard" = global ptr null @main.a = global ptr null +@"main.init$guard" = global ptr null define void @main.init() { _llgo_0: diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index e0f633f2..bc6900e8 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -29,6 +29,7 @@ import ( "github.com/goplus/gogen/packages" "github.com/goplus/llgo/cl" + "github.com/goplus/llgo/internal/llgen" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" @@ -41,7 +42,7 @@ func init() { llssa.SetDebug(llssa.DbgFlagAll) } -func FromDir(t *testing.T, sel, relDir string) { +func FromDir(t *testing.T, sel, relDir string, byLLGen bool) { dir, err := os.Getwd() if err != nil { t.Fatal("Getwd failed:", err) @@ -57,23 +58,30 @@ func FromDir(t *testing.T, sel, relDir string) { continue } t.Run(name, func(t *testing.T) { - testFrom(t, dir+"/"+name, sel) + testFrom(t, dir+"/"+name, sel, byLLGen) }) } } -func testFrom(t *testing.T, pkgDir, sel string) { +func testFrom(t *testing.T, pkgDir, sel string, byLLGen bool) { if sel != "" && !strings.Contains(pkgDir, sel) { return } log.Println("Parsing", pkgDir) in := pkgDir + "/in.go" out := pkgDir + "/out.ll" - expected, err := os.ReadFile(out) + b, err := os.ReadFile(out) if err != nil { t.Fatal("ReadFile failed:", err) } - TestCompileEx(t, nil, in, string(expected)) + expected := string(b) + if byLLGen { + if v := llgen.GenFromFile(in); v != expected { + t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", v, expected) + } + } else { + TestCompileEx(t, nil, in, expected) + } } func TestCompileEx(t *testing.T, src any, fname, expected string) { diff --git a/cl/compile.go b/cl/compile.go index 6f435e8c..60530630 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -25,6 +25,7 @@ import ( "log" "os" "sort" + "strings" llssa "github.com/goplus/llgo/ssa" "golang.org/x/tools/go/ssa" @@ -57,7 +58,7 @@ func SetDebug(dbgFlags dbgFlags) { const ( fnNormal = iota fnHasVArg - fnUnsafeInit + fnIgnore ) func funcKind(vfn ssa.Value) int { @@ -65,8 +66,8 @@ func funcKind(vfn ssa.Value) int { params := fn.Signature.Params() n := params.Len() if n == 0 { - if fn.Name() == "init" && fn.Pkg.Pkg.Path() == "unsafe" { - return fnUnsafeInit + if fn.Name() == "init" && ignorePkgInit(fn.Pkg.Pkg.Path()) { + return fnIgnore } } else { last := params.At(n - 1) @@ -78,6 +79,32 @@ func funcKind(vfn ssa.Value) int { return fnNormal } +func ignorePkgInit(pkgPath string) bool { + switch pkgPath { + case "unsafe", "syscall", "runtime/cgo": + return true + } + return false +} + +func ignoreFunc(name string, fn *ssa.Function) bool { + /* TODO(xsw): confirm this is not needed more + if name == "unsafe.init" { + return true + } + */ + fnName := fn.Name() + if strings.HasPrefix(fnName, "_Cgo_") { + return true + } + const runtime = "runtime" + if strings.HasPrefix(name, runtime) { + left := name[len(runtime):] + return strings.HasPrefix(left, ".cgo") || strings.HasPrefix(left, "/cgo") + } + return false +} + // ----------------------------------------------------------------------------- type none = struct{} @@ -137,14 +164,12 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) { sig := f.Signature name := p.funcName(pkgTypes, f) - /* TODO(xsw): confirm this is not needed more - if name == "unsafe.init" { - return - } - */ if debugInstr { log.Println("==> NewFunc", name, "type:", sig.Recv(), sig) } + if ignoreFunc(name, f) { + return + } fn := pkg.NewFunc(name, sig) p.inits = append(p.inits, func() { p.fn = fn @@ -229,7 +254,7 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l call := v.Call cv := call.Value kind := funcKind(cv) - if kind == fnUnsafeInit { + if kind == fnIgnore { return } if debugGoSSA { @@ -403,17 +428,12 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll val ssa.Member } - // 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. - var members []*namedMember + members := make([]*namedMember, 0, len(pkg.Members)) for name, v := range pkg.Members { members = append(members, &namedMember{name, v}) } sort.Slice(members, func(i, j int) bool { - iPos := members[i].val.Pos() - jPos := members[j].val.Pos() - return iPos < jPos + return members[i].name < members[j].name }) pkgProg := pkg.Prog diff --git a/cl/compile_test.go b/cl/compile_test.go index 6e250588..671f8cba 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -27,8 +27,12 @@ func testCompile(t *testing.T, src, expected string) { cltest.TestCompileEx(t, src, "foo.go", expected) } +func TestFromTestcgo(t *testing.T) { + cltest.FromDir(t, "", "./_testcgo", true) +} + func TestFromTestdata(t *testing.T) { - cltest.FromDir(t, "", "./_testdata") + cltest.FromDir(t, "", "./_testdata", false) } func TestVar(t *testing.T) { @@ -38,8 +42,8 @@ var a int `, `; ModuleID = 'foo' source_filename = "foo" -@"foo.init$guard" = global ptr null @foo.a = global ptr null +@"foo.init$guard" = global ptr null define void @foo.init() { _llgo_0: @@ -67,6 +71,11 @@ source_filename = "foo" @"foo.init$guard" = global ptr null +define i64 @foo.fn(i64 %0, double %1) { +_llgo_0: + ret i64 1 +} + define void @foo.init() { _llgo_0: %0 = load i1, ptr @"foo.init$guard", align 1 @@ -79,10 +88,5 @@ _llgo_1: ; preds = %_llgo_0 _llgo_2: ; preds = %_llgo_1, %_llgo_0 ret void } - -define i64 @foo.fn(i64 %0, double %1) { -_llgo_0: - ret i64 1 -} `) } diff --git a/cl/import.go b/cl/import.go index 5f2faf45..d73b236e 100644 --- a/cl/import.go +++ b/cl/import.go @@ -117,13 +117,20 @@ func fullName(pkg *types.Package, name string) string { } // func: pkg.name -// method: (pkg.T).name +// method: (pkg.T).name, (*pkg.T).name func funcName(pkg *types.Package, fn *ssa.Function) string { sig := fn.Signature + name := fn.Name() if recv := sig.Recv(); recv != nil { - return "(" + recv.Type().String() + ")." + fn.Name() + var tName string + t := recv.Type() + if tp, ok := t.(*types.Pointer); ok { + t, tName = tp.Elem(), "*" + } + tName += t.(*types.Named).Obj().Name() + return "(" + tName + ")." + name } - ret := fullName(pkg, fn.Name()) + ret := fullName(pkg, name) if ret == "main.main" { ret = "main" } diff --git a/internal/build/build.go b/internal/build/build.go index 3eb310aa..0cd2cc27 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -133,7 +133,7 @@ func linkMainPkg(pkg *packages.Package, conf *Config, mode Mode) { args[0] = "-o" args[1] = app packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) { - if p.PkgPath != "unsafe" { // TODO(xsw): remove this special case + if p.PkgPath != "unsafe" { // TODO(xsw): maybe can remove this special case args = append(args, p.ExportFile+".ll") } }) @@ -159,7 +159,7 @@ func buildPkg(prog llssa.Program, aPkg aPackage, mode Mode) { if mode != ModeRun { fmt.Fprintln(os.Stderr, pkgPath) } - if pkgPath == "unsafe" { // TODO(xsw): remove this special case + if pkgPath == "unsafe" { // TODO(xsw): maybe can remove this special case return } ret, err := cl.NewPackage(prog, aPkg.SSA, pkg.Syntax) diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go new file mode 100644 index 00000000..9eca9f8d --- /dev/null +++ b/internal/llgen/llgenf.go @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). 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 llgen + +import ( + "os" + + "github.com/goplus/llgo/cl" + "golang.org/x/tools/go/packages" + "golang.org/x/tools/go/ssa" + "golang.org/x/tools/go/ssa/ssautil" + + llssa "github.com/goplus/llgo/ssa" +) + +const ( + loadFiles = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles + loadImports = loadFiles | packages.NeedImports + loadTypes = loadImports | packages.NeedTypes | packages.NeedTypesSizes + loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo +) + +func GenFromFile(inFile string) string { + cfg := &packages.Config{ + Mode: loadSyntax, + } + initial, err := packages.Load(cfg, inFile) + check(err) + + _, pkgs := ssautil.AllPackages(initial, ssa.SanityCheckFunctions) + + pkg := initial[0] + ssaPkg := pkgs[0] + ssaPkg.Build() + + prog := llssa.NewProgram(nil) + ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax) + check(err) + + return ret.String() +} + +func DoFile(inFile, outFile string) { + ret := GenFromFile(inFile) + err := os.WriteFile(outFile, []byte(ret), 0644) + check(err) +} diff --git a/ssa/cl_test.go b/ssa/cl_test.go index 2dda76c7..db26f209 100644 --- a/ssa/cl_test.go +++ b/ssa/cl_test.go @@ -22,6 +22,10 @@ import ( "github.com/goplus/llgo/cl/cltest" ) -func TestFromTestdata(t *testing.T) { - cltest.FromDir(t, "", "../cl/_testdata") +func TestFromTestcgo(t *testing.T) { + cltest.FromDir(t, "", "../cl/_testcgo", true) +} + +func TestFromTestdata(t *testing.T) { + cltest.FromDir(t, "", "../cl/_testdata", false) } diff --git a/ssa/decl.go b/ssa/decl.go index 1be102ad..18760708 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -145,7 +145,8 @@ func (p Function) Param(i int) Expr { func (p Function) NewBuilder() Builder { prog := p.prog b := prog.ctx.NewBuilder() - b.Finalize() + // TODO(xsw): Finalize may cause panic, so comment it. + // b.Finalize() return &aBuilder{b, p, prog} } diff --git a/ssa/package.go b/ssa/package.go index bf0cc726..b6507c1f 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -122,7 +122,8 @@ func NewProgram(target *Target) Program { target = &Target{} } ctx := llvm.NewContext() - ctx.Finalize() + // TODO(xsw): Finalize may cause panic, so comment it. + // ctx.Finalize() td := llvm.NewTargetData("") // TODO(xsw): target config return &aProgram{ctx: ctx, target: target, td: td} } @@ -130,7 +131,8 @@ func NewProgram(target *Target) Program { // NewPackage creates a new package. func (p Program) NewPackage(name, pkgPath string) Package { mod := p.ctx.NewModule(pkgPath) - mod.Finalize() + // TODO(xsw): Finalize may cause panic, so comment it. + // mod.Finalize() fns := make(map[string]Function) gbls := make(map[string]Global) return &aPackage{mod, fns, gbls, p}