diff --git a/cl/_testdata/importpkg/in.go b/cl/_testdata/importpkg/in.go index f9c1c830..96afbd51 100644 --- a/cl/_testdata/importpkg/in.go +++ b/cl/_testdata/importpkg/in.go @@ -5,5 +5,6 @@ import "github.com/goplus/llgo/cl/internal/stdio" var hello = [...]int8{'H', 'e', 'l', 'l', 'o', '\n', 0} func main() { + _ = stdio.Max(2, 100) stdio.Printf(&hello[0]) } diff --git a/cl/_testdata/importpkg/out.ll b/cl/_testdata/importpkg/out.ll index c07ae22b..de2212b5 100644 --- a/cl/_testdata/importpkg/out.ll +++ b/cl/_testdata/importpkg/out.ll @@ -28,10 +28,13 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0 define void @main() { _llgo_0: call void @main.init() + %0 = call i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64 2, i64 100) call void (ptr, ...) @printf(ptr @main.hello) ret void } declare void @"github.com/goplus/llgo/cl/internal/stdio.init"() +declare i64 @"github.com/goplus/llgo/cl/internal/stdio.Max"(i64, i64) + declare void @printf(ptr, ...) diff --git a/cl/compile.go b/cl/compile.go index c9ebfb86..1b9501c8 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -305,7 +305,8 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll }) pkgTypes := pkg.Pkg - ret = prog.NewPackage(pkgTypes.Name(), pkgTypes.Path()) + pkgName, pkgPath := pkgTypes.Name(), pkgTypes.Path() + ret = prog.NewPackage(pkgName, pkgPath) ctx := &context{ prog: prog, @@ -316,7 +317,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll link: make(map[string]string), loaded: make(map[*types.Package]none), } - ctx.initFiles(pkgTypes.Path(), files) + ctx.initFiles(pkgPath, files) for _, m := range members { member := m.val switch member := member.(type) { diff --git a/cl/import.go b/cl/import.go index 30a203a7..c3f175cb 100644 --- a/cl/import.go +++ b/cl/import.go @@ -106,7 +106,11 @@ func (p *context) initLinkname(pkgPath, line string) { } func fullName(pkg *types.Package, name string) string { - return pkg.Path() + "." + name + pkgPath := pkg.Name() + if pkgPath != "main" { + pkgPath = pkg.Path() + } + return pkgPath + "." + name } func funcName(pkg *types.Package, fn *ssa.Function) string { diff --git a/cl/internal/stdio/printf.go b/cl/internal/stdio/printf.go index 38fffa6f..ac080937 100644 --- a/cl/internal/stdio/printf.go +++ b/cl/internal/stdio/printf.go @@ -8,3 +8,10 @@ const ( //go:linkname Printf printf func Printf(format *int8, __llgo_va_list ...any) + +func Max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/cmd/internal/build/build.go b/cmd/internal/build/build.go index ebe5e4e2..627dcf86 100644 --- a/cmd/internal/build/build.go +++ b/cmd/internal/build/build.go @@ -33,5 +33,7 @@ func init() { } func runCmd(cmd *base.Command, args []string) { - build.Do(args, build.ModeBuild) + build.Do(args, &build.Config{ + Mode: build.ModeBuild, + }) } diff --git a/cmd/internal/install/install.go b/cmd/internal/install/install.go index 1ae49dbf..9f9e6dc2 100644 --- a/cmd/internal/install/install.go +++ b/cmd/internal/install/install.go @@ -33,5 +33,6 @@ func init() { } func runCmd(cmd *base.Command, args []string) { - build.Do(args, build.ModeInstall) + conf := build.NewDefaultConf(build.ModeInstall) + build.Do(args, conf) } diff --git a/internal/build/build_install.go b/internal/build/build.go similarity index 60% rename from internal/build/build_install.go rename to internal/build/build.go index 166fcaf8..49e595e1 100644 --- a/internal/build/build_install.go +++ b/internal/build/build.go @@ -21,14 +21,18 @@ import ( "go/token" "log" "os" + "path" + "path/filepath" + "runtime" "strings" "golang.org/x/tools/go/packages" "golang.org/x/tools/go/ssa" "github.com/goplus/llgo/cl" - llssa "github.com/goplus/llgo/ssa" "github.com/goplus/llgo/x/clang" + + llssa "github.com/goplus/llgo/ssa" ) type Mode int @@ -38,6 +42,27 @@ const ( ModeInstall ) +type Config struct { + BinPath string + AppSuffix string // ".exe" on Windows, empty on Unix + Mode Mode +} + +func NewDefaultConf(mode Mode) *Config { + bin := os.Getenv("GOBIN") + if bin == "" { + bin = filepath.Join(runtime.GOROOT(), "bin") + } + conf := &Config{ + BinPath: bin, + Mode: mode, + } + if runtime.GOOS == "windows" { + conf.AppSuffix = ".exe" + } + return conf +} + // ----------------------------------------------------------------------------- const ( @@ -47,10 +72,10 @@ const ( loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo ) -func Do(args []string, mode Mode) { - flags, patterns := parseArgs(args) +func Do(args []string, conf *Config) { + flags, patterns, verbose := parseArgs(args) cfg := &packages.Config{ - Mode: loadSyntax | packages.NeedExportFile, + Mode: loadSyntax | packages.NeedDeps | packages.NeedExportFile, BuildFlags: flags, } @@ -61,42 +86,64 @@ func Do(args []string, mode Mode) { check(err) // Create SSA-form program representation. - _, pkgs, errPkgs := allPkgs(initial, ssa.SanityCheckFunctions) + ssaProg, pkgs, errPkgs := allPkgs(initial, ssa.SanityCheckFunctions) + ssaProg.Build() for _, errPkg := range errPkgs { log.Println("cannot build SSA for package", errPkg) } llssa.Initialize(llssa.InitAll) - // llssa.SetDebug(llssa.DbgFlagAll) - // cl.SetDebug(cl.DbgFlagAll) + if verbose { + llssa.SetDebug(llssa.DbgFlagAll) + cl.SetDebug(cl.DbgFlagAll) + } prog := llssa.NewProgram(nil) - llFiles := make([]string, 0, len(pkgs)) + mode := conf.Mode for _, pkg := range pkgs { - pkg.SSA.Build() - llFiles = buildPkg(llFiles, prog, pkg, mode) + buildPkg(prog, pkg, mode) } + if mode == ModeInstall { - fmt.Fprintln(os.Stderr, "clang", llFiles) - err = clang.New("").Exec(llFiles...) - check(err) + for _, pkg := range initial { + if pkg.Name == "main" { + linkMainPkg(pkg, conf) + } + } } } -func buildPkg(llFiles []string, prog llssa.Program, pkg aPackage, mode Mode) []string { +func linkMainPkg(pkg *packages.Package, conf *Config) { + name := path.Base(pkg.PkgPath) + args := make([]string, 2, len(pkg.Imports)+3) + args[0] = "-o" + args[1] = filepath.Join(conf.BinPath, name+conf.AppSuffix) + packages.Visit([]*packages.Package{pkg}, nil, func(p *packages.Package) { + if p.PkgPath != "unsafe" { // TODO(xsw): remove this special case + args = append(args, p.ExportFile+".ll") + } + }) + + // TODO(xsw): show work + // fmt.Fprintln(os.Stderr, "clang", args) + fmt.Fprintln(os.Stderr, args[1]) + err := clang.New("").Exec(args...) + check(err) +} + +func buildPkg(prog llssa.Program, aPkg aPackage, mode Mode) { + pkg := aPkg.Package pkgPath := pkg.PkgPath fmt.Fprintln(os.Stderr, pkgPath) if pkgPath == "unsafe" { // TODO(xsw): remove this special case - return llFiles + return } - ret, err := cl.NewPackage(prog, pkg.SSA, pkg.Syntax) + ret, err := cl.NewPackage(prog, aPkg.SSA, pkg.Syntax) check(err) if mode == ModeInstall { file := pkg.ExportFile + ".ll" os.WriteFile(file, []byte(ret.String()), 0644) - llFiles = append(llFiles, file) } - return llFiles } type aPackage struct { @@ -122,13 +169,18 @@ func allPkgs(initial []*packages.Package, mode ssa.BuilderMode) (prog *ssa.Progr return } -func parseArgs(args []string) (flags, patterns []string) { +func parseArgs(args []string) (flags, patterns []string, verbose bool) { for i, arg := range args { if !strings.HasPrefix(arg, "-") { - return args[:i], args[i:] + flags, patterns = args[:i], args[i:] + return + } + if arg == "-v" { + verbose = true } } - return args, nil + flags = args + return } func check(err error) {