[dev.ssa] cmd/compile: preallocate small-numbered values and blocks

Speeds up the compiler ~5%.

Change-Id: Ia5cf0bcd58701fd14018ec77d01f03d5c7d6385b
Reviewed-on: https://go-review.googlesource.com/19060
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index 6e7e10e..6f59134 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -496,6 +496,7 @@
 		if Curfn.Func.Endlineno != 0 {
 			lineno = Curfn.Func.Endlineno
 		}
+		ssafn.Free()
 		return
 	}
 	Genlist(Curfn.Func.Enter)
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index 203de64..ae74732 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -21,6 +21,9 @@
 // Smallest possible faulting page at address zero.
 const minZeroPage = 4096
 
+var ssaConfig *ssa.Config
+var ssaExp ssaExport
+
 func shouldssa(fn *Node) bool {
 	if Thearch.Thestring != "amd64" {
 		return false
@@ -119,9 +122,13 @@
 
 	// TODO(khr): build config just once at the start of the compiler binary
 
-	var e ssaExport
-	e.log = printssa
-	s.config = ssa.NewConfig(Thearch.Thestring, &e, Ctxt, Debug['N'] == 0)
+	ssaExp.log = printssa
+	ssaExp.unimplemented = false
+	ssaExp.mustImplement = true
+	if ssaConfig == nil {
+		ssaConfig = ssa.NewConfig(Thearch.Thestring, &ssaExp, Ctxt, Debug['N'] == 0)
+	}
+	s.config = ssaConfig
 	s.f = s.config.NewFunc()
 	s.f.Name = name
 	s.exitCode = fn.Func.Exit
diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
index b743710..e6f8716 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -219,14 +219,14 @@
 			f.Fatalf("control value for %s is missing: %v", b, b.Control)
 		}
 	}
-	for _, id := range f.bid.free {
-		if blockMark[id] {
-			f.Fatalf("used block b%d in free list", id)
+	for b := f.freeBlocks; b != nil; b = b.Aux.(*Block) {
+		if blockMark[b.ID] {
+			f.Fatalf("used block b%d in free list", b.ID)
 		}
 	}
-	for _, id := range f.vid.free {
-		if valueMark[id] {
-			f.Fatalf("used value v%d in free list", id)
+	for v := f.freeValues; v != nil; v = v.argstorage[0] {
+		if valueMark[v.ID] {
+			f.Fatalf("used value v%d in free list", v.ID)
 		}
 	}
 
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index 7325873..52e772c 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -16,8 +16,13 @@
 	HTML       *HTMLWriter                // html writer, for debugging
 	ctxt       *obj.Link                  // Generic arch information
 	optimize   bool                       // Do optimization
+	curFunc    *Func
 
 	// TODO: more stuff.  Compiler flags of interest, ...
+
+	// Storage for low-numbered values and blocks.
+	values [2000]Value
+	blocks [200]Block
 }
 
 type TypeSource interface {
@@ -100,15 +105,29 @@
 	c.ctxt = ctxt
 	c.optimize = optimize
 
+	// Assign IDs to preallocated values/blocks.
+	for i := range c.values {
+		c.values[i].ID = ID(i)
+	}
+	for i := range c.blocks {
+		c.blocks[i].ID = ID(i)
+	}
+
 	return c
 }
 
 func (c *Config) Frontend() Frontend { return c.fe }
 
-// NewFunc returns a new, empty function object
+// NewFunc returns a new, empty function object.
+// Caller must call f.Free() before calling NewFunc again.
 func (c *Config) NewFunc() *Func {
 	// TODO(khr): should this function take name, type, etc. as arguments?
-	return &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}}
+	if c.curFunc != nil {
+		c.Fatalf(0, "NewFunc called without previous Free")
+	}
+	f := &Func{Config: c, NamedValues: map[LocalSlot][]*Value{}}
+	c.curFunc = f
+	return f
 }
 
 func (c *Config) Logf(msg string, args ...interface{})               { c.fe.Logf(msg, args...) }
@@ -118,6 +137,3 @@
 }
 func (c *Config) Warnl(line int, msg string, args ...interface{}) { c.fe.Warnl(line, msg, args...) }
 func (c *Config) Debug_checknil() bool                            { return c.fe.Debug_checknil() }
-
-// TODO(khr): do we really need a separate Config, or can we just
-// store all its fields inside a Func?
diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go
index 4297082..faf16a3 100644
--- a/src/cmd/compile/internal/ssa/deadcode.go
+++ b/src/cmd/compile/internal/ssa/deadcode.go
@@ -164,7 +164,7 @@
 	f.Names = f.Names[:i]
 
 	// Remove dead values from blocks' value list.  Return dead
-	// value ids to the allocator.
+	// values to the allocator.
 	for _, b := range f.Blocks {
 		i := 0
 		for _, v := range b.Values {
@@ -172,7 +172,7 @@
 				b.Values[i] = v
 				i++
 			} else {
-				f.vid.put(v.ID)
+				f.freeValue(v)
 			}
 		}
 		// aid GC
@@ -197,7 +197,7 @@
 			b.Succs = nil
 			b.Control = nil
 			b.Kind = BlockDead
-			f.bid.put(b.ID)
+			f.freeBlock(b)
 		}
 	}
 	// zero remainder to help GC
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index 371dae3..26e4283 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -4,10 +4,7 @@
 
 package ssa
 
-import (
-	"math"
-	"sync"
-)
+import "math"
 
 // A Func represents a Go func declaration (or function literal) and
 // its body.  This package compiles each Func independently.
@@ -31,6 +28,9 @@
 	// Names is a copy of NamedValues.Keys.  We keep a separate list
 	// of keys to make iteration order deterministic.
 	Names []LocalSlot
+
+	freeValues *Value // free Values linked by argstorage[0].  All other fields except ID are 0/nil.
+	freeBlocks *Block // free Blocks linked by Aux.(*Block).  All other fields except ID are 0/nil.
 }
 
 // NumBlocks returns an integer larger than the id of any Block in the Func.
@@ -43,68 +43,85 @@
 	return f.vid.num()
 }
 
-const (
-	blockSize = 100
-)
-
-// blockPool provides a contiguous array of Blocks which
-// improves the speed of traversing dominator trees.
-type blockPool struct {
-	blocks []Block
-	mu     sync.Mutex
-}
-
-func (bp *blockPool) newBlock() *Block {
-	bp.mu.Lock()
-	defer bp.mu.Unlock()
-
-	if len(bp.blocks) == 0 {
-		bp.blocks = make([]Block, blockSize, blockSize)
+// newValue allocates a new Value with the given fields and places it at the end of b.Values.
+func (f *Func) newValue(op Op, t Type, b *Block, line int32) *Value {
+	var v *Value
+	if f.freeValues != nil {
+		v = f.freeValues
+		f.freeValues = v.argstorage[0]
+		v.argstorage[0] = nil
+	} else {
+		ID := f.vid.get()
+		if int(ID) < len(f.Config.values) {
+			v = &f.Config.values[ID]
+		} else {
+			v = &Value{ID: ID}
+		}
 	}
-
-	res := &bp.blocks[0]
-	bp.blocks = bp.blocks[1:]
-	return res
+	v.Op = op
+	v.Type = t
+	v.Block = b
+	v.Line = line
+	b.Values = append(b.Values, v)
+	return v
 }
 
-var bp blockPool
+// freeValue frees a value.  It must no longer be referenced.
+func (f *Func) freeValue(v *Value) {
+	if v.Type == nil {
+		f.Fatalf("trying to free an already freed value")
+	}
+	// Clear everything but ID (which we reuse).
+	id := v.ID
+	*v = Value{}
+	v.ID = id
+	v.argstorage[0] = f.freeValues
+	f.freeValues = v
+}
 
-// NewBlock returns a new block of the given kind and appends it to f.Blocks.
+// newBlock allocates a new Block of the given kind and places it at the end of f.Blocks.
 func (f *Func) NewBlock(kind BlockKind) *Block {
-	b := bp.newBlock()
-	b.ID = f.bid.get()
+	var b *Block
+	if f.freeBlocks != nil {
+		b = f.freeBlocks
+		f.freeBlocks = b.Aux.(*Block)
+		b.Aux = nil
+	} else {
+		ID := f.bid.get()
+		if int(ID) < len(f.Config.blocks) {
+			b = &f.Config.blocks[ID]
+		} else {
+			b = &Block{ID: ID}
+		}
+	}
 	b.Kind = kind
 	b.Func = f
 	f.Blocks = append(f.Blocks, b)
 	return b
 }
 
+func (f *Func) freeBlock(b *Block) {
+	// Clear everything but ID (which we reuse).
+	id := b.ID
+	*b = Block{}
+	b.ID = id
+	b.Aux = f.freeBlocks
+	f.freeBlocks = b
+}
+
 // NewValue0 returns a new value in the block with no arguments and zero aux values.
 func (b *Block) NewValue0(line int32, op Op, t Type) *Value {
-	v := &Value{
-		ID:    b.Func.vid.get(),
-		Op:    op,
-		Type:  t,
-		Block: b,
-		Line:  line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = 0
 	v.Args = v.argstorage[:0]
-	b.Values = append(b.Values, v)
 	return v
 }
 
 // NewValue returns a new value in the block with no arguments and an auxint value.
 func (b *Block) NewValue0I(line int32, op Op, t Type, auxint int64) *Value {
-	v := &Value{
-		ID:     b.Func.vid.get(),
-		Op:     op,
-		Type:   t,
-		AuxInt: auxint,
-		Block:  b,
-		Line:   line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = auxint
 	v.Args = v.argstorage[:0]
-	b.Values = append(b.Values, v)
 	return v
 }
 
@@ -116,158 +133,93 @@
 		// to prevent errors like using NewValue1A instead of NewValue1I.
 		b.Fatalf("aux field has int64 type op=%s type=%s aux=%v", op, t, aux)
 	}
-	v := &Value{
-		ID:    b.Func.vid.get(),
-		Op:    op,
-		Type:  t,
-		Aux:   aux,
-		Block: b,
-		Line:  line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = 0
+	v.Aux = aux
 	v.Args = v.argstorage[:0]
-	b.Values = append(b.Values, v)
 	return v
 }
 
 // NewValue returns a new value in the block with no arguments and both an auxint and aux values.
 func (b *Block) NewValue0IA(line int32, op Op, t Type, auxint int64, aux interface{}) *Value {
-	v := &Value{
-		ID:     b.Func.vid.get(),
-		Op:     op,
-		Type:   t,
-		AuxInt: auxint,
-		Aux:    aux,
-		Block:  b,
-		Line:   line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = auxint
+	v.Aux = aux
 	v.Args = v.argstorage[:0]
-	b.Values = append(b.Values, v)
 	return v
 }
 
 // NewValue1 returns a new value in the block with one argument and zero aux values.
 func (b *Block) NewValue1(line int32, op Op, t Type, arg *Value) *Value {
-	v := &Value{
-		ID:    b.Func.vid.get(),
-		Op:    op,
-		Type:  t,
-		Block: b,
-		Line:  line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = 0
 	v.Args = v.argstorage[:1]
-	v.Args[0] = arg
-	b.Values = append(b.Values, v)
+	v.argstorage[0] = arg
 	return v
 }
 
 // NewValue1I returns a new value in the block with one argument and an auxint value.
 func (b *Block) NewValue1I(line int32, op Op, t Type, auxint int64, arg *Value) *Value {
-	v := &Value{
-		ID:     b.Func.vid.get(),
-		Op:     op,
-		Type:   t,
-		AuxInt: auxint,
-		Block:  b,
-		Line:   line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = auxint
 	v.Args = v.argstorage[:1]
-	v.Args[0] = arg
-	b.Values = append(b.Values, v)
+	v.argstorage[0] = arg
 	return v
 }
 
 // NewValue1A returns a new value in the block with one argument and an aux value.
 func (b *Block) NewValue1A(line int32, op Op, t Type, aux interface{}, arg *Value) *Value {
-	v := &Value{
-		ID:    b.Func.vid.get(),
-		Op:    op,
-		Type:  t,
-		Aux:   aux,
-		Block: b,
-		Line:  line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = 0
+	v.Aux = aux
 	v.Args = v.argstorage[:1]
-	v.Args[0] = arg
-	b.Values = append(b.Values, v)
+	v.argstorage[0] = arg
 	return v
 }
 
 // NewValue1IA returns a new value in the block with one argument and both an auxint and aux values.
 func (b *Block) NewValue1IA(line int32, op Op, t Type, auxint int64, aux interface{}, arg *Value) *Value {
-	v := &Value{
-		ID:     b.Func.vid.get(),
-		Op:     op,
-		Type:   t,
-		AuxInt: auxint,
-		Aux:    aux,
-		Block:  b,
-		Line:   line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = auxint
+	v.Aux = aux
 	v.Args = v.argstorage[:1]
-	v.Args[0] = arg
-	b.Values = append(b.Values, v)
+	v.argstorage[0] = arg
 	return v
 }
 
 // NewValue2 returns a new value in the block with two arguments and zero aux values.
 func (b *Block) NewValue2(line int32, op Op, t Type, arg0, arg1 *Value) *Value {
-	v := &Value{
-		ID:    b.Func.vid.get(),
-		Op:    op,
-		Type:  t,
-		Block: b,
-		Line:  line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = 0
 	v.Args = v.argstorage[:2]
-	v.Args[0] = arg0
-	v.Args[1] = arg1
-	b.Values = append(b.Values, v)
+	v.argstorage[0] = arg0
+	v.argstorage[1] = arg1
 	return v
 }
 
 // NewValue2I returns a new value in the block with two arguments and an auxint value.
-func (b *Block) NewValue2I(line int32, op Op, t Type, aux int64, arg0, arg1 *Value) *Value {
-	v := &Value{
-		ID:     b.Func.vid.get(),
-		Op:     op,
-		Type:   t,
-		AuxInt: aux,
-		Block:  b,
-		Line:   line,
-	}
+func (b *Block) NewValue2I(line int32, op Op, t Type, auxint int64, arg0, arg1 *Value) *Value {
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = auxint
 	v.Args = v.argstorage[:2]
-	v.Args[0] = arg0
-	v.Args[1] = arg1
-	b.Values = append(b.Values, v)
+	v.argstorage[0] = arg0
+	v.argstorage[1] = arg1
 	return v
 }
 
 // NewValue3 returns a new value in the block with three arguments and zero aux values.
 func (b *Block) NewValue3(line int32, op Op, t Type, arg0, arg1, arg2 *Value) *Value {
-	v := &Value{
-		ID:    b.Func.vid.get(),
-		Op:    op,
-		Type:  t,
-		Block: b,
-		Line:  line,
-	}
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = 0
 	v.Args = []*Value{arg0, arg1, arg2}
-	b.Values = append(b.Values, v)
 	return v
 }
 
 // NewValue3I returns a new value in the block with three arguments and an auxint value.
-func (b *Block) NewValue3I(line int32, op Op, t Type, aux int64, arg0, arg1, arg2 *Value) *Value {
-	v := &Value{
-		ID:     b.Func.vid.get(),
-		Op:     op,
-		Type:   t,
-		AuxInt: aux,
-		Block:  b,
-		Line:   line,
-	}
+func (b *Block) NewValue3I(line int32, op Op, t Type, auxint int64, arg0, arg1, arg2 *Value) *Value {
+	v := b.Func.newValue(op, t, b, line)
+	v.AuxInt = auxint
 	v.Args = []*Value{arg0, arg1, arg2}
-	b.Values = append(b.Values, v)
 	return v
 }
 
@@ -310,3 +262,32 @@
 func (f *Func) Unimplementedf(msg string, args ...interface{}) {
 	f.Config.Unimplementedf(f.Entry.Line, msg, args...)
 }
+
+func (f *Func) Free() {
+	// Clear values.
+	n := f.vid.num()
+	if n > len(f.Config.values) {
+		n = len(f.Config.values)
+	}
+	for i := 1; i < n; i++ {
+		f.Config.values[i] = Value{}
+		f.Config.values[i].ID = ID(i)
+	}
+
+	// Clear blocks.
+	n = f.bid.num()
+	if n > len(f.Config.blocks) {
+		n = len(f.Config.blocks)
+	}
+	for i := 1; i < n; i++ {
+		f.Config.blocks[i] = Block{}
+		f.Config.blocks[i].ID = ID(i)
+	}
+
+	// Unregister from config.
+	if f.Config.curFunc != f {
+		f.Fatalf("free of function which isn't the last one allocated")
+	}
+	f.Config.curFunc = nil
+	*f = Func{} // just in case
+}
diff --git a/src/cmd/compile/internal/ssa/id.go b/src/cmd/compile/internal/ssa/id.go
index 3f53e1a..367e687 100644
--- a/src/cmd/compile/internal/ssa/id.go
+++ b/src/cmd/compile/internal/ssa/id.go
@@ -9,16 +9,10 @@
 // idAlloc provides an allocator for unique integers.
 type idAlloc struct {
 	last ID
-	free []ID
 }
 
 // get allocates an ID and returns it.
 func (a *idAlloc) get() ID {
-	if n := len(a.free); n > 0 {
-		x := a.free[n-1]
-		a.free = a.free[:n-1]
-		return x
-	}
 	x := a.last
 	x++
 	if x == 1<<31-1 {
@@ -28,11 +22,6 @@
 	return x
 }
 
-// put deallocates an ID.
-func (a *idAlloc) put(x ID) {
-	a.free = append(a.free, x)
-}
-
 // num returns the maximum ID ever returned + 1.
 func (a *idAlloc) num() int {
 	return int(a.last + 1)
diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go
index 9238999..2a92624 100644
--- a/src/cmd/compile/internal/ssa/regalloc.go
+++ b/src/cmd/compile/internal/ssa/regalloc.go
@@ -964,9 +964,7 @@
 			// Constants, SP, SB, ...
 			continue
 		}
-		spill.Op = OpInvalid
-		spill.Type = TypeInvalid
-		spill.resetArgs()
+		f.freeValue(spill)
 	}
 	for _, b := range f.Blocks {
 		i := 0