diff --git a/src/cmd/compile/internal/ssa/layout.go b/src/cmd/compile/internal/ssa/layout.go index a2d4785e52..15e111ae7c 100644 --- a/src/cmd/compile/internal/ssa/layout.go +++ b/src/cmd/compile/internal/ssa/layout.go @@ -8,6 +8,33 @@ package ssa // After this phase returns, the order of f.Blocks matters and is the order // in which those blocks will appear in the assembly output. func layout(f *Func) { + f.Blocks = layoutOrder(f) +} + +// Register allocation may use a different order which has constraints +// imposed by the linear-scan algorithm. Note that that f.pass here is +// regalloc, so the switch is conditional on -d=ssa/regalloc/test=N +func layoutRegallocOrder(f *Func) []*Block { + + switch f.pass.test { + case 0: // layout order + return layoutOrder(f) + case 1: // existing block order + return f.Blocks + case 2: // reverse of postorder; legal, but usually not good. + po := f.postorder() + visitOrder := make([]*Block, len(po)) + for i, b := range po { + j := len(po) - i - 1 + visitOrder[j] = b + } + return visitOrder + } + + return nil +} + +func layoutOrder(f *Func) []*Block { order := make([]*Block, 0, f.NumBlocks()) scheduled := make([]bool, f.NumBlocks()) idToBlock := make([]*Block, f.NumBlocks()) @@ -116,5 +143,5 @@ blockloop: } } } - f.Blocks = order + return order } diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index aafa36d64f..abc4e60d7c 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -283,6 +283,9 @@ type regAllocState struct { copies map[*Value]bool loopnest *loopnest + + // choose a good order in which to visit blocks for allocation purposes. + visitOrder []*Block } type endReg struct { @@ -589,11 +592,23 @@ func (s *regAllocState) init(f *Func) { s.allocatable &^= 1 << 15 // X7 disallowed (one 387 register is used as scratch space during SSE->387 generation in ../x86/387.go) } + // Linear scan register allocation can be influenced by the order in which blocks appear. + // Decouple the register allocation order from the generated block order. + // This also creates an opportunity for experiments to find a better order. + s.visitOrder = layoutRegallocOrder(f) + + // Compute block order. This array allows us to distinguish forward edges + // from backward edges and compute how far they go. + blockOrder := make([]int32, f.NumBlocks()) + for i, b := range s.visitOrder { + blockOrder[b.ID] = int32(i) + } + s.regs = make([]regState, s.numRegs) s.values = make([]valState, f.NumValues()) s.orig = make([]*Value, f.NumValues()) s.copies = make(map[*Value]bool) - for _, b := range f.Blocks { + for _, b := range s.visitOrder { for _, v := range b.Values { if !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() && !v.Type.IsTuple() { s.values[v.ID].needReg = true @@ -606,16 +621,9 @@ func (s *regAllocState) init(f *Func) { } s.computeLive() - // Compute block order. This array allows us to distinguish forward edges - // from backward edges and compute how far they go. - blockOrder := make([]int32, f.NumBlocks()) - for i, b := range f.Blocks { - blockOrder[b.ID] = int32(i) - } - // Compute primary predecessors. s.primary = make([]int32, f.NumBlocks()) - for _, b := range f.Blocks { + for _, b := range s.visitOrder { best := -1 for i, e := range b.Preds { p := e.b @@ -728,7 +736,7 @@ func (s *regAllocState) regalloc(f *Func) { f.Fatalf("entry block must be first") } - for _, b := range f.Blocks { + for _, b := range s.visitOrder { if s.f.pass.debug > regDebug { fmt.Printf("Begin processing block %v\n", b) } @@ -1544,7 +1552,7 @@ func (s *regAllocState) regalloc(f *Func) { } } - for _, b := range f.Blocks { + for _, b := range s.visitOrder { i := 0 for _, v := range b.Values { if v.Op == OpInvalid { @@ -1562,7 +1570,7 @@ func (s *regAllocState) placeSpills() { // Precompute some useful info. phiRegs := make([]regMask, f.NumBlocks()) - for _, b := range f.Blocks { + for _, b := range s.visitOrder { var m regMask for _, v := range b.Values { if v.Op != OpPhi { @@ -1672,7 +1680,7 @@ func (s *regAllocState) placeSpills() { // Insert spill instructions into the block schedules. var oldSched []*Value - for _, b := range f.Blocks { + for _, b := range s.visitOrder { nphi := 0 for _, v := range b.Values { if v.Op != OpPhi { @@ -1701,7 +1709,7 @@ func (s *regAllocState) shuffle(stacklive [][]ID) { fmt.Println(s.f.String()) } - for _, b := range s.f.Blocks { + for _, b := range s.visitOrder { if len(b.Preds) <= 1 { continue }