From ec63158d7104ab6eb3765f7d4ea48744f97d9ff9 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 11 Jan 2017 15:48:30 -0800 Subject: [PATCH] [dev.inline] cmd/compile: parse source files concurrently Conversion to Nodes still happens sequentially at the moment. Change-Id: I3407ba0711b8b92e22ece0a06fefaff863c3ccc9 Reviewed-on: https://go-review.googlesource.com/35126 Run-TryBot: Matthew Dempsky Reviewed-by: Robert Griesemer TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/gc/lex.go | 30 ++----- src/cmd/compile/internal/gc/noder.go | 111 ++++++++++++++++---------- src/cmd/compile/internal/gc/syntax.go | 17 ++-- 3 files changed, 87 insertions(+), 71 deletions(-) diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 5ff55d7c83..c0039fd880 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -40,17 +40,15 @@ func plan9quote(s string) string { return s } -type Pragma syntax.Pragma - const ( // Func pragmas. - Nointerface Pragma = 1 << iota - Noescape // func parameters don't escape - Norace // func must not have race detector annotations - Nosplit // func should not execute on separate stack - Noinline // func should not be inlined - CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all - UintptrEscapes // pointers converted to uintptr escape + Nointerface syntax.Pragma = 1 << iota + Noescape // func parameters don't escape + Norace // func must not have race detector annotations + Nosplit // func should not execute on separate stack + Noinline // func should not be inlined + CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all + UintptrEscapes // pointers converted to uintptr escape // Runtime-only func pragmas. // See ../../../../runtime/README.md for detailed descriptions. @@ -63,7 +61,7 @@ const ( NotInHeap // values of this type must not be heap allocated ) -func pragmaValue(verb string) Pragma { +func pragmaValue(verb string) syntax.Pragma { switch verb { case "go:nointerface": if obj.Fieldtrack_enabled != 0 { @@ -78,24 +76,12 @@ func pragmaValue(verb string) Pragma { case "go:noinline": return Noinline case "go:systemstack": - if !compiling_runtime { - yyerror("//go:systemstack only allowed in runtime") - } return Systemstack case "go:nowritebarrier": - if !compiling_runtime { - yyerror("//go:nowritebarrier only allowed in runtime") - } return Nowritebarrier case "go:nowritebarrierrec": - if !compiling_runtime { - yyerror("//go:nowritebarrierrec only allowed in runtime") - } return Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier case "go:yeswritebarrierrec": - if !compiling_runtime { - yyerror("//go:yeswritebarrierrec only allowed in runtime") - } return Yeswritebarrierrec case "go:cgo_unsafe_args": return CgoUnsafeArgs diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 3744da6165..1207c3f614 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -18,34 +18,47 @@ import ( func parseFiles(filenames []string) uint { var lines uint + var noders []*noder + for _, filename := range filenames { - lines += parseFile(filename) + p := &noder{err: make(chan syntax.Error)} + noders = append(noders, p) + + go func(filename string) { + defer close(p.err) + base := src.NewFileBase(filename, absFilename(filename)) + + f, err := os.Open(filename) + if err != nil { + p.error(syntax.Error{Pos: src.MakePos(base, 0, 0), Msg: err.Error()}) + return + } + defer f.Close() + + p.file, _ = syntax.Parse(base, f, p.error, p.pragma, 0) // errors are tracked via p.error + }(filename) + } + + for _, p := range noders { + for e := range p.err { + yyerrorpos(e.Pos, "%s", e.Msg) + } + + p.node() + lines += p.file.Lines + p.file = nil // release memory + if nsyntaxerrors != 0 { errorexit() } - } - return lines -} - -func parseFile(filename string) uint { - f, err := os.Open(filename) - if err != nil { - fmt.Println(err) - errorexit() - } - defer f.Close() - - base := src.NewFileBase(filename, absFilename(filename)) - var p noder - file, _ := syntax.Parse(base, f, p.error, p.pragma, 0) // errors are tracked via p.error - - p.file(file) - - if nsyntaxerrors == 0 { testdclstack() } - return file.Lines + return lines +} + +func yyerrorpos(pos src.Pos, format string, args ...interface{}) { + yyerrorl(Ctxt.PosTable.XPos(pos), format, args...) } var pathPrefix string @@ -54,27 +67,41 @@ func absFilename(name string) string { return obj.AbsFile(Ctxt.Pathname, name, pathPrefix) } -// noder transforms package syntax's AST into a Nod tree. +// noder transforms package syntax's AST into a Node tree. type noder struct { - linknames []src.Pos // tracks //go:linkname positions + file *syntax.File + linknames []linkname + pragcgobuf string + err chan syntax.Error } -func (p *noder) file(file *syntax.File) { +// linkname records a //go:linkname directive. +type linkname struct { + pos src.Pos + local string + remote string +} + +func (p *noder) node() { block = 1 iota_ = -1000000 imported_unsafe = false - p.lineno(file.PkgName) - mkpackage(file.PkgName.Value) + p.lineno(p.file.PkgName) + mkpackage(p.file.PkgName.Value) - xtop = append(xtop, p.decls(file.DeclList)...) + xtop = append(xtop, p.decls(p.file.DeclList)...) - if !imported_unsafe { - for _, pos := range p.linknames { - p.error(syntax.Error{Pos: pos, Msg: "//go:linkname only allowed in Go files that import \"unsafe\""}) + for _, n := range p.linknames { + if imported_unsafe { + lookup(n.local).Linkname = n.remote + } else { + yyerrorpos(n.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") } } + pragcgobuf += p.pragcgobuf + // For compatibility with old code only (comparisons w/ toolstash): // The old line number tracking simply continued incrementing the // virtual line number (lexlineno) and using it also for lineno. @@ -84,7 +111,7 @@ func (p *noder) file(file *syntax.File) { // for fninit and set lineno to NoPos here. // TODO(gri) fix this once we switched permanently to the new // position information. - lineno = MakePos(file.Pos().Base(), uint(file.Lines), 0) + lineno = MakePos(p.file.Pos().Base(), uint(p.file.Lines), 0) clearImports() } @@ -221,7 +248,7 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node { func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node { name := typedcl0(p.name(decl.Name)) - name.Name.Param.Pragma = Pragma(decl.Pragma) + name.Name.Param.Pragma = decl.Pragma var typ *Node if decl.Type != nil { @@ -258,7 +285,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node { } } - pragma := Pragma(fun.Pragma) + pragma := fun.Pragma f.Nbody.Set(body) f.Noescape = pragma&Noescape != 0 @@ -1043,8 +1070,7 @@ func (p *noder) lineno(n syntax.Node) { } func (p *noder) error(err error) { - e := err.(syntax.Error) - yyerrorl(Ctxt.PosTable.XPos(e.Pos), "%s", e.Msg) + p.err <- err.(syntax.Error) } func (p *noder) pragma(pos src.Pos, text string) syntax.Pragma { @@ -1054,26 +1080,27 @@ func (p *noder) pragma(pos src.Pos, text string) syntax.Pragma { panic("unreachable") case strings.HasPrefix(text, "go:linkname "): - // Record line number so we can emit an error later if - // the file doesn't import package unsafe. - p.linknames = append(p.linknames, pos) - f := strings.Fields(text) if len(f) != 3 { p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname linkname"}) break } - lookup(f[1]).Linkname = f[2] + p.linknames = append(p.linknames, linkname{pos, f[1], f[2]}) case strings.HasPrefix(text, "go:cgo_"): - pragcgobuf += pragcgo(text) + p.pragcgobuf += pragcgo(text) fallthrough // because of //go:cgo_unsafe_args default: verb := text if i := strings.Index(text, " "); i >= 0 { verb = verb[:i] } - return syntax.Pragma(pragmaValue(verb)) + prag := pragmaValue(verb) + const runtimePragmas = Systemstack | Nowritebarrier | Nowritebarrierrec | Yeswritebarrierrec + if !compiling_runtime && prag&runtimePragmas != 0 { + p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//go:%s only allowed in runtime", verb)}) + } + return prag } return 0 diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 8be9f21df2..3039aeb402 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -6,7 +6,10 @@ package gc -import "cmd/internal/src" +import ( + "cmd/compile/internal/syntax" + "cmd/internal/src" +) // A Node is a single node in the syntax tree. // Actually the syntax tree is a syntax DAG, because there is only one @@ -285,7 +288,7 @@ type Param struct { // OTYPE pragmas // // TODO: Should Func pragmas also be stored on the Name? - Pragma Pragma + Pragma syntax.Pragma } // Func holds Node fields used only with function-like nodes. @@ -313,11 +316,11 @@ type Func struct { Endlineno src.XPos WBPos src.XPos // position of first write barrier - Pragma Pragma // go:xxx function annotations - Dupok bool // duplicate definitions ok - Wrapper bool // is method wrapper - Needctxt bool // function uses context register (has closure variables) - ReflectMethod bool // function calls reflect.Type.Method or MethodByName + Pragma syntax.Pragma // go:xxx function annotations + Dupok bool // duplicate definitions ok + Wrapper bool // is method wrapper + Needctxt bool // function uses context register (has closure variables) + ReflectMethod bool // function calls reflect.Type.Method or MethodByName IsHiddenClosure bool NoFramePointer bool // Must not use a frame pointer for this function }