From 9741d83ceaf046e55077d68719cc2781cd96d5f1 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 18 May 2016 13:27:11 -0700 Subject: [PATCH] cmd/asm, go/build: invoke cmd/asm only once per package Prior to this CL, cmd/go invoked cmd/asm once for every assembly file. The exec and cmd/asm startup overhead dwarfed the actual time spent assembling. This CL adds support to cmd/asm to process multiple input files and uses it in cmd/go. This cuts 10% off the wall time for 'go build -a math'. Fixes #15680 Change-Id: I12d2ee2c817207954961dc8f37b8f2b09f835550 Reviewed-on: https://go-review.googlesource.com/27636 Run-TryBot: Josh Bleecher Snyder TryBot-Result: Gobot Gobot Reviewed-by: Rob Pike --- src/cmd/asm/internal/flags/flags.go | 9 +++-- src/cmd/asm/main.go | 30 ++++++++++------ src/cmd/go/build.go | 55 +++++++++++++++++------------ 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go index 4557c2a7f9..bd90b82bf6 100644 --- a/src/cmd/asm/internal/flags/flags.go +++ b/src/cmd/asm/internal/flags/flags.go @@ -15,7 +15,7 @@ import ( var ( Debug = flag.Bool("debug", false, "dump instructions as they are parsed") - OutputFile = flag.String("o", "", "output file; default foo.6 for /a/b/c/foo.s on amd64") + OutputFile = flag.String("o", "", "output file; default foo.o for /a/b/c/foo.s as first argument") PrintOut = flag.Bool("S", false, "print assembly and machine code") TrimPath = flag.String("trimpath", "", "remove prefix from recorded source file paths") Shared = flag.Bool("shared", false, "generate code that can be linked into a shared library") @@ -49,7 +49,7 @@ func (m *MultiFlag) Set(val string) error { } func Usage() { - fmt.Fprintf(os.Stderr, "usage: asm [options] file.s\n") + fmt.Fprintf(os.Stderr, "usage: asm [options] file.s ...\n") fmt.Fprintf(os.Stderr, "Flags:\n") flag.PrintDefaults() os.Exit(2) @@ -58,12 +58,15 @@ func Usage() { func Parse() { flag.Usage = Usage flag.Parse() - if flag.NArg() != 1 { + if flag.NArg() == 0 { flag.Usage() } // Flag refinement. if *OutputFile == "" { + if flag.NArg() != 1 { + flag.Usage() + } input := filepath.Base(flag.Arg(0)) if strings.HasSuffix(input, ".s") { input = input[:len(input)-2] diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index c612583e6b..0959732728 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -54,22 +54,32 @@ func main() { fmt.Fprintf(buf, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion()) fmt.Fprintf(buf, "!\n") - lexer := lex.NewLexer(flag.Arg(0), ctxt) - parser := asm.NewParser(ctxt, architecture, lexer) - diag := false - ctxt.DiagFunc = func(format string, args ...interface{}) { - diag = true - log.Printf(format, args...) + var ok, diag bool + var failedFile string + for _, f := range flag.Args() { + lexer := lex.NewLexer(f, ctxt) + parser := asm.NewParser(ctxt, architecture, lexer) + ctxt.DiagFunc = func(format string, args ...interface{}) { + diag = true + log.Printf(format, args...) + } + pList := obj.Linknewplist(ctxt) + pList.Firstpc, ok = parser.Parse() + if !ok { + failedFile = f + break + } } - pList := obj.Linknewplist(ctxt) - var ok bool - pList.Firstpc, ok = parser.Parse() if ok { // reports errors to parser.Errorf obj.Writeobjdirect(ctxt, buf) } if !ok || diag { - log.Printf("assembly of %s failed", flag.Arg(0)) + if failedFile != "" { + log.Printf("assembly of %s failed", failedFile) + } else { + log.Print("assembly failed") + } os.Remove(*flags.OutputFile) os.Exit(1) } diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index bb76465ce7..b5df9a22c4 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -1561,12 +1561,12 @@ func (b *builder) build(a *action) (err error) { } // Assemble .s files. - for _, file := range sfiles { - out := file[:len(file)-len(".s")] + ".o" - if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil { + if len(sfiles) > 0 { + ofiles, err := buildToolchain.asm(b, a.p, obj, sfiles) + if err != nil { return err } - objects = append(objects, out) + objects = append(objects, ofiles...) } // NOTE(rsc): On Windows, it is critically important that the @@ -2203,9 +2203,9 @@ type toolchain interface { // cc runs the toolchain's C compiler in a directory on a C file // to produce an output file. cc(b *builder, p *Package, objdir, ofile, cfile string) error - // asm runs the assembler in a specific directory on a specific file - // to generate the named output file. - asm(b *builder, p *Package, obj, ofile, sfile string) error + // asm runs the assembler in a specific directory on specific files + // and returns a list of named output files. + asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) // pkgpath builds an appropriate path for a temporary package file. pkgpath(basedir string, p *Package) string // pack runs the archive packer in a specific directory to create @@ -2242,8 +2242,8 @@ func (noToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, return "", nil, noCompiler() } -func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { - return noCompiler() +func (noToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) { + return nil, noCompiler() } func (noToolchain) pkgpath(basedir string, p *Package) string { @@ -2340,10 +2340,10 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, return ofile, output, err } -func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { +func (gcToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) { // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files. inc := filepath.Join(goroot, "pkg", "include") - sfile = mkAbs(p.Dir, sfile) + ofile := obj + "asm.o" args := []interface{}{buildToolExec, tool("asm"), "-o", ofile, "-trimpath", b.work, "-I", obj, "-I", inc, "-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch, buildAsmflags} if p.ImportPath == "runtime" && goarch == "386" { for _, arg := range buildAsmflags { @@ -2352,11 +2352,13 @@ func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { } } } - args = append(args, sfile) + for _, sfile := range sfiles { + args = append(args, mkAbs(p.Dir, sfile)) + } if err := b.run(p.Dir, p.ImportPath, nil, args...); err != nil { - return err + return nil, err } - return nil + return []string{ofile}, nil } // toolVerify checks that the command line args writes the same output file @@ -2623,15 +2625,24 @@ func (tools gccgoToolchain) gc(b *builder, p *Package, archive, obj string, asmh return ofile, output, err } -func (tools gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { - sfile = mkAbs(p.Dir, sfile) - defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} - if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { - defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath) +func (tools gccgoToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) { + var ofiles []string + for _, sfile := range sfiles { + ofile := obj + sfile[:len(sfile)-len(".s")] + ".o" + ofiles = append(ofiles, ofile) + sfile = mkAbs(p.Dir, sfile) + defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} + if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { + defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath) + } + defs = tools.maybePIC(defs) + defs = append(defs, b.gccArchArgs()...) + err := b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", obj, "-c", "-o", ofile, defs, sfile) + if err != nil { + return nil, err + } } - defs = tools.maybePIC(defs) - defs = append(defs, b.gccArchArgs()...) - return b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", obj, "-c", "-o", ofile, defs, sfile) + return ofiles, nil } func (gccgoToolchain) pkgpath(basedir string, p *Package) string { -- 2.48.1