From 5aa5db7593537e43b024d65b07f2a9c3379f100e Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Fri, 11 Mar 2016 13:39:20 -0500 Subject: [PATCH] cmd/compile: use bufio.Reader directly in lexer Removes an intermediate layer of functions that was clogging up a corner of the compiler's profile graph. I can't measure a performance improvement running a large build like jujud, but the profile reports less total time spent in gc.(*lexer).getr. Change-Id: I3000585cfcb0f9729d3a3859e9023690a6528591 Reviewed-on: https://go-review.googlesource.com/20565 Reviewed-by: Robert Griesemer Run-TryBot: David Crawshaw TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/gc/bimport.go | 20 ++-- src/cmd/compile/internal/gc/export.go | 3 +- src/cmd/compile/internal/gc/lex.go | 123 +++++++++++-------------- src/cmd/compile/internal/gc/parser.go | 8 +- src/cmd/internal/obj/util.go | 23 ----- test/syntax/ddd.go | 11 +++ 6 files changed, 79 insertions(+), 109 deletions(-) create mode 100644 test/syntax/ddd.go diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index e6f76e7251..a68281f5a7 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -8,8 +8,8 @@ package gc import ( + "bufio" "cmd/compile/internal/big" - "cmd/internal/obj" "encoding/binary" "fmt" ) @@ -20,7 +20,7 @@ import ( // changes to bimport.go and bexport.go. // Import populates importpkg from the serialized package data. -func Import(in *obj.Biobuf) { +func Import(in *bufio.Reader) { p := importer{in: in} p.buf = p.bufarray[:] @@ -137,7 +137,7 @@ func idealType(typ *Type) *Type { } type importer struct { - in *obj.Biobuf + in *bufio.Reader buf []byte // for reading strings bufarray [64]byte // initial underlying array for buf, large enough to avoid allocation when compiling std lib pkgList []*Pkg @@ -855,16 +855,16 @@ func (p *importer) ReadByte() (byte, error) { // byte is the bottleneck interface for reading from p.in. // It unescapes '|' 'S' to '$' and '|' '|' to '|'. func (p *importer) byte() byte { - c := obj.Bgetc(p.in) + c, err := p.in.ReadByte() p.read++ - if c < 0 { - Fatalf("importer: read error") + if err != nil { + Fatalf("importer: read error: %v", err) } if c == '|' { - c = obj.Bgetc(p.in) + c, err = p.in.ReadByte() p.read++ - if c < 0 { - Fatalf("importer: read error") + if err != nil { + Fatalf("importer: read error: %v", err) } switch c { case 'S': @@ -875,5 +875,5 @@ func (p *importer) byte() byte { Fatalf("importer: unexpected escape sequence in export data") } } - return byte(c) + return c } diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 9a1f1a6aaf..69b969dfdf 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -5,6 +5,7 @@ package gc import ( + "bufio" "bytes" "cmd/internal/obj" "fmt" @@ -387,7 +388,7 @@ func dumpexport() { pkgMap = make(map[string]*Pkg) pkgs = nil importpkg = mkpkg("") - Import(obj.Binitr(©)) // must not die + Import(bufio.NewReader(©)) // must not die importpkg = nil pkgs = savedPkgs pkgMap = savedPkgMap diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index b9bbe55973..f8ec00079f 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -7,6 +7,7 @@ package gc import ( + "bufio" "cmd/compile/internal/ssa" "cmd/internal/obj" "flag" @@ -335,15 +336,16 @@ func Main() { linehistpush(infile) - bin, err := obj.Bopenr(infile) + f, err := os.Open(infile) if err != nil { fmt.Printf("open %s: %v\n", infile, err) errorexit() } + bin := bufio.NewReader(f) // Skip initial BOM if present. - if obj.Bgetrune(bin) != BOM { - obj.Bungetrune(bin) + if r, _, _ := bin.ReadRune(); r != BOM { + bin.UnreadRune() } block = 1 @@ -362,7 +364,7 @@ func Main() { lexlineno++ linehistpop() - obj.Bterm(bin) + f.Close() } testdclstack() @@ -541,7 +543,7 @@ func saveerrors() { nerrors = 0 } -func arsize(b *obj.Biobuf, name string) int { +func arsize(b *bufio.Reader, name string) int { var buf [ArhdrSize]byte if _, err := io.ReadFull(b, buf[:]); err != nil { return -1 @@ -555,14 +557,11 @@ func arsize(b *obj.Biobuf, name string) int { return i } -func skiptopkgdef(b *obj.Biobuf) bool { +func skiptopkgdef(b *bufio.Reader) bool { // archive header - p := obj.Brdline(b, '\n') - if p == "" { - return false - } - if obj.Blinelen(b) != 8 { - return false + p, err := b.ReadString('\n') + if err != nil { + log.Fatalf("reading input: %v", err) } if p != "!\n" { return false @@ -672,10 +671,10 @@ func loadsys() { incannedimport = 1 importpkg = Runtimepkg - parse_import(obj.Binitr(strings.NewReader(runtimeimport)), nil) + parse_import(bufio.NewReader(strings.NewReader(runtimeimport)), nil) importpkg = unsafepkg - parse_import(obj.Binitr(strings.NewReader(unsafeimport)), nil) + parse_import(bufio.NewReader(strings.NewReader(unsafeimport)), nil) importpkg = nil incannedimport = 0 @@ -761,12 +760,13 @@ func importfile(f *Val, indent []byte) { importpkg.Imported = true - imp, err := obj.Bopenr(file) + impf, err := os.Open(file) if err != nil { Yyerror("can't open import: %q: %v", path_, err) errorexit() } - defer obj.Bterm(imp) + defer impf.Close() + imp := bufio.NewReader(impf) if strings.HasSuffix(file, ".a") { if !skiptopkgdef(imp) { @@ -776,7 +776,13 @@ func importfile(f *Val, indent []byte) { } // check object header - p := obj.Brdstr(imp, '\n', 1) + p, err := imp.ReadString('\n') + if err != nil { + log.Fatalf("reading input: %v", err) + } + if len(p) > 0 { + p = p[:len(p)-1] + } if p != "empty archive" { if !strings.HasPrefix(p, "go object ") { @@ -800,23 +806,23 @@ func importfile(f *Val, indent []byte) { // $$B\n (new format): import directly, then feed the lexer a dummy statement // look for $$ - var c int + var c byte for { - c = obj.Bgetc(imp) - if c < 0 { + c, err = imp.ReadByte() + if err != nil { break } if c == '$' { - c = obj.Bgetc(imp) - if c == '$' || c < 0 { + c, err = imp.ReadByte() + if c == '$' || err != nil { break } } } // get character after $$ - if c >= 0 { - c = obj.Bgetc(imp) + if err == nil { + c, _ = imp.ReadByte() } switch c { @@ -826,7 +832,7 @@ func importfile(f *Val, indent []byte) { case 'B': // new export format - obj.Bgetc(imp) // skip \n after $$B + imp.ReadByte() // skip \n after $$B Import(imp) default: @@ -879,9 +885,7 @@ const ( type lexer struct { // source - bin *obj.Biobuf - peekr1 rune - peekr2 rune // second peekc for ... + bin *bufio.Reader nlsemi bool // if set, '\n' and EOF translate to ';' @@ -1025,8 +1029,9 @@ l0: } if c1 == '.' { - c1 = l.getr() - if c1 == '.' { + p, err := l.bin.Peek(1) + if err == nil && p[0] == '.' { + l.getr() c = LDDD goto lx } @@ -1886,49 +1891,26 @@ func pragcgo(text string) { } func (l *lexer) getr() rune { - // unread rune != 0 available - if r := l.peekr1; r != 0 { - l.peekr1 = l.peekr2 - l.peekr2 = 0 - if r == '\n' && importpkg == nil { - lexlineno++ - } - return r - } - redo: - // common case: 7bit ASCII - c := obj.Bgetc(l.bin) - if c < utf8.RuneSelf { - if c == 0 { - yyerrorl(lexlineno, "illegal NUL byte") - return 0 + r, w, err := l.bin.ReadRune() + if err != nil { + if err != io.EOF { + Fatalf("io error: %v", err) } - if c == '\n' && importpkg == nil { + return -1 + } + switch r { + case 0: + yyerrorl(lexlineno, "illegal NUL byte") + case '\n': + if importpkg == nil { lexlineno++ } - return rune(c) - } - // c >= utf8.RuneSelf - - // uncommon case: non-ASCII - var buf [utf8.UTFMax]byte - buf[0] = byte(c) - buf[1] = byte(obj.Bgetc(l.bin)) - i := 2 - for ; i < len(buf) && !utf8.FullRune(buf[:i]); i++ { - buf[i] = byte(obj.Bgetc(l.bin)) - } - - r, w := utf8.DecodeRune(buf[:i]) - if r == utf8.RuneError && w == 1 { - // The string conversion here makes a copy for passing - // to fmt.Printf, so that buf itself does not escape and - // can be allocated on the stack. - yyerrorl(lexlineno, "illegal UTF-8 sequence % x", string(buf[:i])) - } - - if r == BOM { + case utf8.RuneError: + if w == 1 { + yyerrorl(lexlineno, "illegal UTF-8 sequence") + } + case BOM: yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file") goto redo } @@ -1937,8 +1919,7 @@ redo: } func (l *lexer) ungetr(r rune) { - l.peekr2 = l.peekr1 - l.peekr1 = r + l.bin.UnreadRune() if r == '\n' && importpkg == nil { lexlineno-- } diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go index fa7e70c43f..b2584c80d6 100644 --- a/src/cmd/compile/internal/gc/parser.go +++ b/src/cmd/compile/internal/gc/parser.go @@ -13,7 +13,7 @@ package gc // to handle optional commas and semicolons before a closing ) or } . import ( - "cmd/internal/obj" + "bufio" "fmt" "strconv" "strings" @@ -22,12 +22,12 @@ import ( const trace = false // if set, parse tracing can be enabled with -x // parse_import parses the export data of a package that is imported. -func parse_import(bin *obj.Biobuf, indent []byte) { +func parse_import(bin *bufio.Reader, indent []byte) { newparser(bin, indent).import_package() } // parse_file parses a single Go source file. -func parse_file(bin *obj.Biobuf) { +func parse_file(bin *bufio.Reader) { newparser(bin, nil).file() } @@ -40,7 +40,7 @@ type parser struct { // newparser returns a new parser ready to parse from src. // indent is the initial indentation for tracing output. -func newparser(src *obj.Biobuf, indent []byte) *parser { +func newparser(src *bufio.Reader, indent []byte) *parser { var p parser p.bin = src p.indent = indent diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index 4c37f76ca9..bd533a0036 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -129,18 +129,6 @@ func Bgetc(b *Biobuf) int { return int(c) } -func Bgetrune(b *Biobuf) int { - r, _, err := b.r.ReadRune() - if err != nil { - return -1 - } - return int(r) -} - -func Bungetrune(b *Biobuf) { - b.r.UnreadRune() -} - func (b *Biobuf) Read(p []byte) (int, error) { return b.r.Read(p) } @@ -158,17 +146,6 @@ func Brdline(b *Biobuf, delim int) string { return string(s) } -func Brdstr(b *Biobuf, delim int, cut int) string { - s, err := b.r.ReadString(byte(delim)) - if err != nil { - log.Fatalf("reading input: %v", err) - } - if len(s) > 0 && cut > 0 { - s = s[:len(s)-1] - } - return s -} - func Blinelen(b *Biobuf) int { return b.linelen } diff --git a/test/syntax/ddd.go b/test/syntax/ddd.go new file mode 100644 index 0000000000..476ae22793 --- /dev/null +++ b/test/syntax/ddd.go @@ -0,0 +1,11 @@ +// errorcheck + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func f() { + g(f..3) // ERROR "unexpected literal \.3, expecting name or \(" +}