From: Todd Neal Date: Wed, 16 Mar 2016 02:14:04 +0000 (-0500) Subject: cmd/compile: reuse blocks in critical pass X-Git-Tag: go1.7beta1~1259 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=50bc546d432632ba948efd43927973ea752549ed;p=gostls13.git cmd/compile: reuse blocks in critical pass If a phi has duplicate arguments, then the new block that is constructed to remove the critical edge can be used for all of the duplicate arguments. read-only data = -904 bytes (-0.058308%) global text (code) = -2240 bytes (-0.060056%) Total difference -3144 bytes (-0.056218%) Change-Id: Iee3762744d6a8c9d26cdfa880bb23feb62b03c9c Reviewed-on: https://go-review.googlesource.com/20746 Run-TryBot: Todd Neal Reviewed-by: Keith Randall TryBot-Result: Gobot Gobot --- diff --git a/src/cmd/compile/internal/ssa/critical.go b/src/cmd/compile/internal/ssa/critical.go index 9230306785..b414913f94 100644 --- a/src/cmd/compile/internal/ssa/critical.go +++ b/src/cmd/compile/internal/ssa/critical.go @@ -8,28 +8,85 @@ package ssa // more than one outedge to a block with more than one inedge). // Regalloc wants a critical-edge-free CFG so it can implement phi values. func critical(f *Func) { - for _, b := range f.Blocks { + // maps from phi arg ID to the new block created for that argument + blocks := make([]*Block, f.NumValues()) + // need to iterate over f.Blocks without range, as we might + // need to split critical edges on newly constructed blocks + for j := 0; j < len(f.Blocks); j++ { + b := f.Blocks[j] if len(b.Preds) <= 1 { continue } + var phi *Value + // determine if we've only got a single phi in this + // block, this is easier to handle than the general + // case of a block with multiple phi values. + for _, v := range b.Values { + if v.Op == OpPhi { + if phi != nil { + phi = nil + break + } + phi = v + } + } + + // reset our block map + if phi != nil { + for _, v := range phi.Args { + blocks[v.ID] = nil + } + } + // split input edges coming from multi-output blocks. - for i, c := range b.Preds { + for i := 0; i < len(b.Preds); i++ { + c := b.Preds[i] if c.Kind == BlockPlain { continue // only single output block } - // allocate a new block to place on the edge - d := f.NewBlock(BlockPlain) - d.Line = c.Line - if f.pass.debug > 0 { - f.Config.Warnl(c.Line, "split critical edge") + var d *Block // new block used to remove critical edge + reusedBlock := false // if true, then this is not the first use of this block + if phi != nil { + argID := phi.Args[i].ID + // find or record the block that we used to split + // critical edges for this argument + if d = blocks[argID]; d == nil { + d = f.NewBlock(BlockPlain) + d.Line = c.Line + blocks[argID] = d + if f.pass.debug > 0 { + f.Config.Warnl(c.Line, "split critical edge") + } + } else { + reusedBlock = true + } + } else { + // no existing block, so allocate a new block + // to place on the edge + d = f.NewBlock(BlockPlain) + d.Line = c.Line + if f.pass.debug > 0 { + f.Config.Warnl(c.Line, "split critical edge") + } + } + + // if this not the first argument for the + // block, then we need to remove the + // corresponding elements from the block + // predecessors and phi args + if reusedBlock { + d.Preds = append(d.Preds, c) + b.Preds[i] = nil + phi.Args[i] = nil + } else { + // splice it in + d.Preds = append(d.Preds, c) + d.Succs = append(d.Succs, b) + b.Preds[i] = d } - // splice it in - d.Preds = append(d.Preds, c) - d.Succs = append(d.Succs, b) - b.Preds[i] = d // replace b with d in c's successor list. for j, b2 := range c.Succs { if b2 == b { @@ -38,5 +95,33 @@ func critical(f *Func) { } } } + + // clean up phi's args and b's predecessor list + if phi != nil { + phi.Args = filterNilValues(phi.Args) + b.Preds = filterNilBlocks(b.Preds) + } + } +} + +// filterNilValues preserves the order of v, while filtering out nils. +func filterNilValues(v []*Value) []*Value { + nv := v[:0] + for i := range v { + if v[i] != nil { + nv = append(nv, v[i]) + } + } + return nv +} + +// filterNilBlocks preserves the order of b, while filtering out nils. +func filterNilBlocks(b []*Block) []*Block { + nb := b[:0] + for i := range b { + if b[i] != nil { + nb = append(nb, b[i]) + } } + return nb }