| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ssa |
| |
| // critical splits critical edges (those that go from a block with |
| // 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) { |
| // 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 := 0; i < len(b.Preds); { |
| e := b.Preds[i] |
| p := e.b |
| pi := e.i |
| if p.Kind == BlockPlain { |
| i++ |
| continue // only single output block |
| } |
| |
| 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 { |
| // splitting doesn't necessarily remove the critical edge, |
| // since we're iterating over len(f.Blocks) above, this forces |
| // the new blocks to be re-examined. |
| d = f.NewBlock(BlockPlain) |
| d.Line = p.Line |
| blocks[argID] = d |
| if f.pass.debug > 0 { |
| f.Config.Warnl(p.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 = p.Line |
| if f.pass.debug > 0 { |
| f.Config.Warnl(p.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 { |
| // Add p->d edge |
| p.Succs[pi] = Edge{d, len(d.Preds)} |
| d.Preds = append(d.Preds, Edge{p, pi}) |
| |
| // Remove p as a predecessor from b. |
| b.removePred(i) |
| |
| // Update corresponding phi args |
| n := len(b.Preds) |
| phi.Args[i].Uses-- |
| phi.Args[i] = phi.Args[n] |
| phi.Args[n] = nil |
| phi.Args = phi.Args[:n] |
| // splitting occasionally leads to a phi having |
| // a single argument (occurs with -N) |
| if n == 1 { |
| phi.Op = OpCopy |
| } |
| // Don't increment i in this case because we moved |
| // an unprocessed predecessor down into slot i. |
| } else { |
| // splice it in |
| p.Succs[pi] = Edge{d, 0} |
| b.Preds[i] = Edge{d, 0} |
| d.Preds = append(d.Preds, Edge{p, pi}) |
| d.Succs = append(d.Succs, Edge{b, i}) |
| i++ |
| } |
| } |
| } |
| } |