cmd/compile: track pragmas in lexer rather than global variables

By using a Pragma bit set (8 bits) rather than 8 booleans, also
reduce Func type size by 8 bytes (208B -> 200B on 64bit platforms,
116B -> 108B on 32bit platforms).

Change-Id: Ibb7e1f8c418a0b5bc6ff813cbdde7bc6f0013b5a
Reviewed-on: https://go-review.googlesource.com/19966
Reviewed-by: Dave Cheney <dave@cheney.net>
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 731f31b..f330f1b 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -240,10 +240,9 @@
 			{
 				saved := structpkg
 				structpkg = tsym.Pkg
-				addmethod(sym, n.Type, false, nointerface)
+				addmethod(sym, n.Type, false, false)
 				structpkg = saved
 			}
-			nointerface = false
 			funchdr(n)
 
 			// (comment from go.y)
diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go
index fdeb6e6..7be050d 100644
--- a/src/cmd/compile/internal/gc/cgen.go
+++ b/src/cmd/compile/internal/gc/cgen.go
@@ -781,7 +781,7 @@
 
 func cgen_wbptr(n, res *Node) {
 	if Curfn != nil {
-		if Curfn.Func.Nowritebarrier {
+		if Curfn.Func.Pragma&Nowritebarrier != 0 {
 			Yyerror("write barrier prohibited")
 		}
 		if Curfn.Func.WBLineno == 0 {
@@ -831,7 +831,7 @@
 
 func cgen_wbfat(n, res *Node) {
 	if Curfn != nil {
-		if Curfn.Func.Nowritebarrier {
+		if Curfn.Func.Pragma&Nowritebarrier != 0 {
 			Yyerror("write barrier prohibited")
 		}
 		if Curfn.Func.WBLineno == 0 {
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index ccbb2d9..e485f9d 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -1530,7 +1530,7 @@
 
 		// Check nowritebarrierrec functions.
 		for _, n := range list {
-			if !n.Func.Nowritebarrierrec {
+			if n.Func.Pragma&Nowritebarrierrec == 0 {
 				continue
 			}
 			call, hasWB := c.best[n]
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index a6fe894..dbf3a97 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -616,23 +616,10 @@
 // when the race detector is enabled.
 var instrumenting bool
 
-// Pending annotations for next func declaration.
-var (
-	noescape          bool
-	noinline          bool
-	norace            bool
-	nosplit           bool
-	nowritebarrier    bool
-	nowritebarrierrec bool
-	systemstack       bool
-)
-
 var debuglive int
 
 var Ctxt *obj.Link
 
-var nointerface bool
-
 var writearchive int
 
 var bstdout obj.Biobuf
diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
index cae15f9..17cc61a 100644
--- a/src/cmd/compile/internal/gc/inl.go
+++ b/src/cmd/compile/internal/gc/inl.go
@@ -107,7 +107,7 @@
 	}
 
 	// If marked "go:noinline", don't inline
-	if fn.Func.Noinline {
+	if fn.Func.Pragma&Noinline != 0 {
 		return
 	}
 
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index d6a18c7..c15fefb 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -844,6 +844,19 @@
 	return s
 }
 
+type Pragma uint8
+
+const (
+	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
+	Systemstack              // func must run on system stack
+	Nowritebarrier           // emit compiler error instead of write barrier
+	Nowritebarrierrec        // error on write barrier in this or recursive callees
+)
+
 type lexer struct {
 	// source
 	bin    *obj.Biobuf
@@ -852,6 +865,10 @@
 
 	nlsemi bool // if set, '\n' and EOF translate to ';'
 
+	// pragma flags
+	// accumulated by lexer; reset by parser
+	pragma Pragma
+
 	// current token
 	tok  int32
 	sym_ *Sym // valid if tok == LNAME
@@ -1650,32 +1667,31 @@
 			Lookup(f[1]).Linkname = f[2]
 		case "go:nointerface":
 			if obj.Fieldtrack_enabled != 0 {
-				nointerface = true
+				l.pragma |= Nointerface
 			}
 		case "go:noescape":
-			noescape = true
+			l.pragma |= Noescape
 		case "go:norace":
-			norace = true
+			l.pragma |= Norace
 		case "go:nosplit":
-			nosplit = true
+			l.pragma |= Nosplit
 		case "go:noinline":
-			noinline = true
+			l.pragma |= Noinline
 		case "go:systemstack":
 			if compiling_runtime == 0 {
 				Yyerror("//go:systemstack only allowed in runtime")
 			}
-			systemstack = true
+			l.pragma |= Systemstack
 		case "go:nowritebarrier":
 			if compiling_runtime == 0 {
 				Yyerror("//go:nowritebarrier only allowed in runtime")
 			}
-			nowritebarrier = true
+			l.pragma |= Nowritebarrier
 		case "go:nowritebarrierrec":
 			if compiling_runtime == 0 {
 				Yyerror("//go:nowritebarrierrec only allowed in runtime")
 			}
-			nowritebarrierrec = true
-			nowritebarrier = true // Implies nowritebarrier
+			l.pragma |= Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
 		}
 		return c
 	}
diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go
index 5a67a3f..d425723 100644
--- a/src/cmd/compile/internal/gc/parser.go
+++ b/src/cmd/compile/internal/gc/parser.go
@@ -1893,25 +1893,21 @@
 	}
 
 	p.want(LFUNC)
-	f := p.fndcl()
+	f := p.fndcl(p.pragma&Nointerface != 0)
 	body := p.fnbody()
 
 	if f == nil {
 		return nil
 	}
-	if noescape && body != nil {
-		Yyerror("can only use //go:noescape with external func implementations")
-	}
 
 	f.Nbody = body
+	f.Noescape = p.pragma&Noescape != 0
+	if f.Noescape && body != nil {
+		Yyerror("can only use //go:noescape with external func implementations")
+	}
+	f.Func.Pragma = p.pragma
 	f.Func.Endlineno = lineno
-	f.Noescape = noescape
-	f.Func.Norace = norace
-	f.Func.Nosplit = nosplit
-	f.Func.Noinline = noinline
-	f.Func.Nowritebarrier = nowritebarrier
-	f.Func.Nowritebarrierrec = nowritebarrierrec
-	f.Func.Systemstack = systemstack
+
 	funcbody(f)
 
 	return f
@@ -1922,7 +1918,7 @@
 // Function     = Signature FunctionBody .
 // MethodDecl   = "func" Receiver MethodName ( Function | Signature ) .
 // Receiver     = Parameters .
-func (p *parser) fndcl() *Node {
+func (p *parser) fndcl(nointerface bool) *Node {
 	if trace && Debug['x'] != 0 {
 		defer p.trace("fndcl")()
 	}
@@ -2058,8 +2054,7 @@
 		ss.Type = functype(s2.N, s6, s8)
 
 		checkwidth(ss.Type)
-		addmethod(s4, ss.Type, false, nointerface)
-		nointerface = false
+		addmethod(s4, ss.Type, false, false)
 		funchdr(ss)
 
 		// inl.C's inlnode in on a dotmeth node expects to find the inlineable body as
@@ -2140,18 +2135,10 @@
 			testdclstack()
 		}
 
-		noescape = false
-		noinline = false
-		nointerface = false
-		norace = false
-		nosplit = false
-		nowritebarrier = false
-		nowritebarrierrec = false
-		systemstack = false
+		// Reset p.pragma BEFORE advancing to the next token (consuming ';')
+		// since comments before may set pragmas for the next function decl.
+		p.pragma = 0
 
-		// Consume ';' AFTER resetting the above flags since
-		// it may read the subsequent comment line which may
-		// set the flags for the next function declaration.
 		if p.tok != EOF && !p.got(';') {
 			p.syntax_error("after top level declaration")
 			p.advance(LVAR, LCONST, LTYPE, LFUNC)
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index a44cc73..475d8e7 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -434,10 +434,10 @@
 	if fn.Func.Needctxt {
 		ptxt.From3.Offset |= obj.NEEDCTXT
 	}
-	if fn.Func.Nosplit {
+	if fn.Func.Pragma&Nosplit != 0 {
 		ptxt.From3.Offset |= obj.NOSPLIT
 	}
-	if fn.Func.Systemstack {
+	if fn.Func.Pragma&Systemstack != 0 {
 		ptxt.From.Sym.Cfunc = 1
 	}
 
diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
index 8a6eba3..d1ae6be 100644
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/gc/racewalk.go
@@ -50,7 +50,7 @@
 }
 
 func instrument(fn *Node) {
-	if ispkgin(omit_pkgs) || fn.Func.Norace {
+	if ispkgin(omit_pkgs) || fn.Func.Pragma&Norace != 0 {
 		return
 	}
 
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index adf447d..83ee4ae 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -169,18 +169,12 @@
 	Depth   int32
 
 	Endlineno int32
+	WBLineno  int32 // line number of first write barrier
 
-	Norace            bool // func must not have race detector annotations
-	Nosplit           bool // func should not execute on separate stack
-	Noinline          bool // func should not be inlined
-	Nowritebarrier    bool // emit compiler error instead of write barrier
-	Nowritebarrierrec bool // error on write barrier in this or recursive callees
-	Dupok             bool // duplicate definitions ok
-	Wrapper           bool // is method wrapper
-	Needctxt          bool // function uses context register (has closure variables)
-	Systemstack       bool // must run on system stack
-
-	WBLineno int32 // line number 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)
 }
 
 type Op uint8