Commit 9741d83c authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

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 <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRob Pike <r@golang.org>
parent 9d4623fe
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
var ( var (
Debug = flag.Bool("debug", false, "dump instructions as they are parsed") 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") PrintOut = flag.Bool("S", false, "print assembly and machine code")
TrimPath = flag.String("trimpath", "", "remove prefix from recorded source file paths") 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") 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 { ...@@ -49,7 +49,7 @@ func (m *MultiFlag) Set(val string) error {
} }
func Usage() { 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") fmt.Fprintf(os.Stderr, "Flags:\n")
flag.PrintDefaults() flag.PrintDefaults()
os.Exit(2) os.Exit(2)
...@@ -58,12 +58,15 @@ func Usage() { ...@@ -58,12 +58,15 @@ func Usage() {
func Parse() { func Parse() {
flag.Usage = Usage flag.Usage = Usage
flag.Parse() flag.Parse()
if flag.NArg() != 1 { if flag.NArg() == 0 {
flag.Usage() flag.Usage()
} }
// Flag refinement. // Flag refinement.
if *OutputFile == "" { if *OutputFile == "" {
if flag.NArg() != 1 {
flag.Usage()
}
input := filepath.Base(flag.Arg(0)) input := filepath.Base(flag.Arg(0))
if strings.HasSuffix(input, ".s") { if strings.HasSuffix(input, ".s") {
input = input[:len(input)-2] input = input[:len(input)-2]
......
...@@ -54,22 +54,32 @@ func main() { ...@@ -54,22 +54,32 @@ func main() {
fmt.Fprintf(buf, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion()) fmt.Fprintf(buf, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion())
fmt.Fprintf(buf, "!\n") fmt.Fprintf(buf, "!\n")
lexer := lex.NewLexer(flag.Arg(0), ctxt) var ok, diag bool
parser := asm.NewParser(ctxt, architecture, lexer) var failedFile string
diag := false for _, f := range flag.Args() {
ctxt.DiagFunc = func(format string, args ...interface{}) { lexer := lex.NewLexer(f, ctxt)
diag = true parser := asm.NewParser(ctxt, architecture, lexer)
log.Printf(format, args...) 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 { if ok {
// reports errors to parser.Errorf // reports errors to parser.Errorf
obj.Writeobjdirect(ctxt, buf) obj.Writeobjdirect(ctxt, buf)
} }
if !ok || diag { 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.Remove(*flags.OutputFile)
os.Exit(1) os.Exit(1)
} }
......
...@@ -1561,12 +1561,12 @@ func (b *builder) build(a *action) (err error) { ...@@ -1561,12 +1561,12 @@ func (b *builder) build(a *action) (err error) {
} }
// Assemble .s files. // Assemble .s files.
for _, file := range sfiles { if len(sfiles) > 0 {
out := file[:len(file)-len(".s")] + ".o" ofiles, err := buildToolchain.asm(b, a.p, obj, sfiles)
if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil { if err != nil {
return err return err
} }
objects = append(objects, out) objects = append(objects, ofiles...)
} }
// NOTE(rsc): On Windows, it is critically important that the // NOTE(rsc): On Windows, it is critically important that the
...@@ -2203,9 +2203,9 @@ type toolchain interface { ...@@ -2203,9 +2203,9 @@ type toolchain interface {
// cc runs the toolchain's C compiler in a directory on a C file // cc runs the toolchain's C compiler in a directory on a C file
// to produce an output file. // to produce an output file.
cc(b *builder, p *Package, objdir, ofile, cfile string) error cc(b *builder, p *Package, objdir, ofile, cfile string) error
// asm runs the assembler in a specific directory on a specific file // asm runs the assembler in a specific directory on specific files
// to generate the named output file. // and returns a list of named output files.
asm(b *builder, p *Package, obj, ofile, sfile string) error asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error)
// pkgpath builds an appropriate path for a temporary package file. // pkgpath builds an appropriate path for a temporary package file.
pkgpath(basedir string, p *Package) string pkgpath(basedir string, p *Package) string
// pack runs the archive packer in a specific directory to create // 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, ...@@ -2242,8 +2242,8 @@ func (noToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
return "", nil, noCompiler() return "", nil, noCompiler()
} }
func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { func (noToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) {
return noCompiler() return nil, noCompiler()
} }
func (noToolchain) pkgpath(basedir string, p *Package) string { func (noToolchain) pkgpath(basedir string, p *Package) string {
...@@ -2340,10 +2340,10 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, ...@@ -2340,10 +2340,10 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
return ofile, output, err 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. // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
inc := filepath.Join(goroot, "pkg", "include") 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} 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" { if p.ImportPath == "runtime" && goarch == "386" {
for _, arg := range buildAsmflags { for _, arg := range buildAsmflags {
...@@ -2352,11 +2352,13 @@ func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { ...@@ -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 { 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 // 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 ...@@ -2623,15 +2625,24 @@ func (tools gccgoToolchain) gc(b *builder, p *Package, archive, obj string, asmh
return ofile, output, err return ofile, output, err
} }
func (tools gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { func (tools gccgoToolchain) asm(b *builder, p *Package, obj string, sfiles []string) ([]string, error) {
sfile = mkAbs(p.Dir, sfile) var ofiles []string
defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} for _, sfile := range sfiles {
if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { ofile := obj + sfile[:len(sfile)-len(".s")] + ".o"
defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath) 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) return ofiles, nil
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)
} }
func (gccgoToolchain) pkgpath(basedir string, p *Package) string { func (gccgoToolchain) pkgpath(basedir string, p *Package) string {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment