]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile: preallocate small-numbered values and blocks
authorKeith Randall <khr@golang.org>
Thu, 28 Jan 2016 21:46:30 +0000 (13:46 -0800)
committerKeith Randall <khr@golang.org>
Thu, 28 Jan 2016 22:52:42 +0000 (22:52 +0000)
Speeds up the compiler ~5%.

Change-Id: Ia5cf0bcd58701fd14018ec77d01f03d5c7d6385b
Reviewed-on: https://go-review.googlesource.com/19060
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/check.go
src/cmd/compile/internal/ssa/config.go
src/cmd/compile/internal/ssa/deadcode.go
src/cmd/compile/internal/ssa/func.go
src/cmd/compile/internal/ssa/id.go
src/cmd/compile/internal/ssa/regalloc.go

index 6e7e10e163817a9f39c86db78c7fe22048f92159..6f5913406e0d4ab82d3fa03282ce302bf6dfde33 100644 (file)
@@ -496,6 +496,7 @@ func compile(fn *Node) {
                if Curfn.Func.Endlineno != 0 {
                        lineno = Curfn.Func.Endlineno
                }
+               ssafn.Free()
                return
        }
        Genlist(Curfn.Func.Enter)
index 203de6421cf4cab2affaf92df07bb82a1cf521c6..ae747324be1d8b7c65d659cee6989bcb09336bb5 100644 (file)
@@ -21,6 +21,9 @@ import (
 // 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 @@ func buildssa(fn *Node) *ssa.Func {
 
        // 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
index b74371008c04f487d4a169ac212e754366782e81..e6f8716d5b23a9fbf1675ea4af692b6236bb78b2 100644 (file)
@@ -219,14 +219,14 @@ func checkFunc(f *Func) {
                        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)
                }
        }
 
index 7325873a1555bc704198158022d61ece9413c4b1..52e772ce81050496c8b82055307260e989a7b6d6 100644 (file)
@@ -16,8 +16,13 @@ type Config struct {
        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 @@ func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config
        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) Unimplementedf(line int32, msg string, args ...interface{}) {
 }
 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?
index 429708213f52a6ff5465b222d040afdd80070710..faf16a381677b4a5b127fe5046b2e67bcfe09e1b 100644 (file)
@@ -164,7 +164,7 @@ func deadcode(f *Func) {
        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 @@ func deadcode(f *Func) {
                                b.Values[i] = v
                                i++
                        } else {
-                               f.vid.put(v.ID)
+                               f.freeValue(v)
                        }
                }
                // aid GC
@@ -197,7 +197,7 @@ func deadcode(f *Func) {
                        b.Succs = nil
                        b.Control = nil
                        b.Kind = BlockDead
-                       f.bid.put(b.ID)
+                       f.freeBlock(b)
                }
        }
        // zero remainder to help GC
index 371dae3b17c9f95a7bb982ab70a1920aa6f5d3b6..26e4283a238bb4ce9848dbd4791a26d0d91361e2 100644 (file)
@@ -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 @@ type Func struct {
        // 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 @@ func (f *Func) NumValues() int {
        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
+// 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}
+               }
+       }
+       v.Op = op
+       v.Type = t
+       v.Block = b
+       v.Line = line
+       b.Values = append(b.Values, v)
+       return v
 }
 
-func (bp *blockPool) newBlock() *Block {
-       bp.mu.Lock()
-       defer bp.mu.Unlock()
-
-       if len(bp.blocks) == 0 {
-               bp.blocks = make([]Block, blockSize, blockSize)
+// 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")
        }
-
-       res := &bp.blocks[0]
-       bp.blocks = bp.blocks[1:]
-       return res
+       // Clear everything but ID (which we reuse).
+       id := v.ID
+       *v = Value{}
+       v.ID = id
+       v.argstorage[0] = f.freeValues
+       f.freeValues = v
 }
 
-var bp blockPool
-
-// 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 @@ func (b *Block) NewValue0A(line int32, op Op, t Type, aux interface{}) *Value {
                // 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) Fatalf(msg string, args ...interface{}) { f.Config.Fatalf(f.Entry
 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
+}
index 3f53e1a434663ea23f1e352c2daa9718a325b6c6..367e687abfaf8c9fc169fb025ffc06517ff2f45b 100644 (file)
@@ -9,16 +9,10 @@ type ID int32
 // 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 @@ func (a *idAlloc) get() ID {
        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)
index 92389990746eb6c794412c93a6b68761cc6505bf..2a92624319bffc2534f677d654e4f9d00eea5e8b 100644 (file)
@@ -964,9 +964,7 @@ func (s *regAllocState) regalloc(f *Func) {
                        // Constants, SP, SB, ...
                        continue
                }
-               spill.Op = OpInvalid
-               spill.Type = TypeInvalid
-               spill.resetArgs()
+               f.freeValue(spill)
        }
        for _, b := range f.Blocks {
                i := 0