Commit 4fa05002 authored by Keith Randall's avatar Keith Randall

cmd/compile: enable constant-time CFG editing

Provide indexes along with block pointers for Preds
and Succs arrays.  This allows us to splice edges in
and out of those arrays in constant time.

Fixes worst-case O(n^2) behavior in deadcode and fuse.

benchmark                     old ns/op      new ns/op     delta
BenchmarkFuse1-8              2065           2057          -0.39%
BenchmarkFuse10-8             9408           9073          -3.56%
BenchmarkFuse100-8            105238         76277         -27.52%
BenchmarkFuse1000-8           3982562        1026750       -74.22%
BenchmarkFuse10000-8          301220329      12824005      -95.74%
BenchmarkDeadCode1-8          1588           1566          -1.39%
BenchmarkDeadCode10-8         4333           4250          -1.92%
BenchmarkDeadCode100-8        32031          32574         +1.70%
BenchmarkDeadCode1000-8       590407         468275        -20.69%
BenchmarkDeadCode10000-8      17822890       5000818       -71.94%
BenchmarkDeadCode100000-8     1388706640     78021127      -94.38%
BenchmarkDeadCode200000-8     5372518479     168598762     -96.86%

Change-Id: Iccabdbb9343fd1c921ba07bbf673330a1c36ee17
Reviewed-on: https://go-review.googlesource.com/22589Reviewed-by: default avatarJosh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent bcd4b84b
...@@ -882,7 +882,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { ...@@ -882,7 +882,7 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// Optimization - if the subsequent block has a load or store // Optimization - if the subsequent block has a load or store
// at the same address, we don't need to issue this instruction. // at the same address, we don't need to issue this instruction.
mem := v.Args[1] mem := v.Args[1]
for _, w := range v.Block.Succs[0].Values { for _, w := range v.Block.Succs[0].Block().Values {
if w.Op == ssa.OpPhi { if w.Op == ssa.OpPhi {
if w.Type.IsMemory() { if w.Type.IsMemory() {
mem = w mem = w
...@@ -978,10 +978,10 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) { ...@@ -978,10 +978,10 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
switch b.Kind { switch b.Kind {
case ssa.BlockPlain, ssa.BlockCall, ssa.BlockCheck: case ssa.BlockPlain, ssa.BlockCall, ssa.BlockCheck:
if b.Succs[0] != next { if b.Succs[0].Block() != next {
p := gc.Prog(obj.AJMP) p := gc.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0]}) s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
} }
case ssa.BlockDefer: case ssa.BlockDefer:
// defer returns in rax: // defer returns in rax:
...@@ -994,11 +994,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) { ...@@ -994,11 +994,11 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
p.To.Reg = x86.REG_AX p.To.Reg = x86.REG_AX
p = gc.Prog(x86.AJNE) p = gc.Prog(x86.AJNE)
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1]}) s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0] != next { if b.Succs[0].Block() != next {
p := gc.Prog(obj.AJMP) p := gc.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0]}) s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
} }
case ssa.BlockExit: case ssa.BlockExit:
gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here
...@@ -1025,22 +1025,22 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) { ...@@ -1025,22 +1025,22 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
likely := b.Likely likely := b.Likely
var p *obj.Prog var p *obj.Prog
switch next { switch next {
case b.Succs[0]: case b.Succs[0].Block():
p = gc.Prog(jmp.invasm) p = gc.Prog(jmp.invasm)
likely *= -1 likely *= -1
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1]}) s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
case b.Succs[1]: case b.Succs[1].Block():
p = gc.Prog(jmp.asm) p = gc.Prog(jmp.asm)
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0]}) s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
default: default:
p = gc.Prog(jmp.asm) p = gc.Prog(jmp.asm)
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0]}) s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
q := gc.Prog(obj.AJMP) q := gc.Prog(obj.AJMP)
q.To.Type = obj.TYPE_BRANCH q.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1]}) s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()})
} }
// liblink reorders the instruction stream as it sees fit. // liblink reorders the instruction stream as it sees fit.
......
...@@ -136,19 +136,19 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) { ...@@ -136,19 +136,19 @@ func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
switch b.Kind { switch b.Kind {
case ssa.BlockCall: case ssa.BlockCall:
if b.Succs[0] != next { if b.Succs[0].Block() != next {
p := gc.Prog(obj.AJMP) p := gc.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0]}) s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
} }
case ssa.BlockRet: case ssa.BlockRet:
gc.Prog(obj.ARET) gc.Prog(obj.ARET)
case ssa.BlockARMLT: case ssa.BlockARMLT:
p := gc.Prog(arm.ABLT) p := gc.Prog(arm.ABLT)
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0]}) s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
p = gc.Prog(obj.AJMP) p = gc.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1]}) s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
} }
} }
...@@ -3799,7 +3799,8 @@ func (s *state) resolveFwdRef(v *ssa.Value) { ...@@ -3799,7 +3799,8 @@ func (s *state) resolveFwdRef(v *ssa.Value) {
// Find variable value on each predecessor. // Find variable value on each predecessor.
var argstore [4]*ssa.Value var argstore [4]*ssa.Value
args := argstore[:0] args := argstore[:0]
for _, p := range b.Preds { for _, e := range b.Preds {
p := e.Block()
args = append(args, s.lookupVarOutgoing(p, v.Type, name, v.Line)) args = append(args, s.lookupVarOutgoing(p, v.Type, name, v.Line))
} }
...@@ -4044,7 +4045,7 @@ func oneFPJump(b *ssa.Block, jumps *FloatingEQNEJump, likely ssa.BranchPredictio ...@@ -4044,7 +4045,7 @@ func oneFPJump(b *ssa.Block, jumps *FloatingEQNEJump, likely ssa.BranchPredictio
p := Prog(jumps.Jump) p := Prog(jumps.Jump)
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
to := jumps.Index to := jumps.Index
branches = append(branches, Branch{p, b.Succs[to]}) branches = append(branches, Branch{p, b.Succs[to].Block()})
if to == 1 { if to == 1 {
likely = -likely likely = -likely
} }
...@@ -4066,10 +4067,10 @@ func oneFPJump(b *ssa.Block, jumps *FloatingEQNEJump, likely ssa.BranchPredictio ...@@ -4066,10 +4067,10 @@ func oneFPJump(b *ssa.Block, jumps *FloatingEQNEJump, likely ssa.BranchPredictio
func SSAGenFPJump(s *SSAGenState, b, next *ssa.Block, jumps *[2][2]FloatingEQNEJump) { func SSAGenFPJump(s *SSAGenState, b, next *ssa.Block, jumps *[2][2]FloatingEQNEJump) {
likely := b.Likely likely := b.Likely
switch next { switch next {
case b.Succs[0]: case b.Succs[0].Block():
s.Branches = oneFPJump(b, &jumps[0][0], likely, s.Branches) s.Branches = oneFPJump(b, &jumps[0][0], likely, s.Branches)
s.Branches = oneFPJump(b, &jumps[0][1], likely, s.Branches) s.Branches = oneFPJump(b, &jumps[0][1], likely, s.Branches)
case b.Succs[1]: case b.Succs[1].Block():
s.Branches = oneFPJump(b, &jumps[1][0], likely, s.Branches) s.Branches = oneFPJump(b, &jumps[1][0], likely, s.Branches)
s.Branches = oneFPJump(b, &jumps[1][1], likely, s.Branches) s.Branches = oneFPJump(b, &jumps[1][1], likely, s.Branches)
default: default:
...@@ -4077,7 +4078,7 @@ func SSAGenFPJump(s *SSAGenState, b, next *ssa.Block, jumps *[2][2]FloatingEQNEJ ...@@ -4077,7 +4078,7 @@ func SSAGenFPJump(s *SSAGenState, b, next *ssa.Block, jumps *[2][2]FloatingEQNEJ
s.Branches = oneFPJump(b, &jumps[1][1], likely, s.Branches) s.Branches = oneFPJump(b, &jumps[1][1], likely, s.Branches)
q := Prog(obj.AJMP) q := Prog(obj.AJMP)
q.To.Type = obj.TYPE_BRANCH q.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, Branch{q, b.Succs[1]}) s.Branches = append(s.Branches, Branch{q, b.Succs[1].Block()})
} }
} }
......
...@@ -12,18 +12,30 @@ type Block struct { ...@@ -12,18 +12,30 @@ type Block struct {
// these IDs densely, but no guarantees. // these IDs densely, but no guarantees.
ID ID ID ID
// Line number for block's control operation
Line int32
// The kind of block this is. // The kind of block this is.
Kind BlockKind Kind BlockKind
// Likely direction for branches.
// If BranchLikely, Succs[0] is the most likely branch taken.
// If BranchUnlikely, Succs[1] is the most likely branch taken.
// Ignored if len(Succs) < 2.
// Fatal if not BranchUnknown and len(Succs) > 2.
Likely BranchPrediction
// After flagalloc, records whether flags are live at the end of the block.
FlagsLiveAtEnd bool
// Subsequent blocks, if any. The number and order depend on the block kind. // Subsequent blocks, if any. The number and order depend on the block kind.
// All successors must be distinct (to make phi values in successors unambiguous). Succs []Edge
Succs []*Block
// Inverse of successors. // Inverse of successors.
// The order is significant to Phi nodes in the block. // The order is significant to Phi nodes in the block.
Preds []*Block
// TODO: predecessors is a pain to maintain. Can we somehow order phi // TODO: predecessors is a pain to maintain. Can we somehow order phi
// arguments by block id and have this field computed explicitly when needed? // arguments by block id and have this field computed explicitly when needed?
Preds []Edge
// A value that determines how the block is exited. Its value depends on the kind // A value that determines how the block is exited. Its value depends on the kind
// of the block. For instance, a BlockIf has a boolean control value and BlockExit // of the block. For instance, a BlockIf has a boolean control value and BlockExit
...@@ -41,23 +53,41 @@ type Block struct { ...@@ -41,23 +53,41 @@ type Block struct {
// The containing function // The containing function
Func *Func Func *Func
// Line number for block's control operation // Storage for Succs, Preds, and Values
Line int32 succstorage [2]Edge
predstorage [4]Edge
// Likely direction for branches. valstorage [9]*Value
// If BranchLikely, Succs[0] is the most likely branch taken. }
// If BranchUnlikely, Succs[1] is the most likely branch taken.
// Ignored if len(Succs) < 2.
// Fatal if not BranchUnknown and len(Succs) > 2.
Likely BranchPrediction
// After flagalloc, records whether flags are live at the end of the block. // Edge represents a CFG edge.
FlagsLiveAtEnd bool // Example edges for b branching to either c or d.
// (c and d have other predecessors.)
// b.Succs = [{c,3}, {d,1}]
// c.Preds = [?, ?, ?, {b,0}]
// d.Preds = [?, {b,1}, ?]
// These indexes allow us to edit the CFG in constant time.
// In addition, it informs phi ops in degenerate cases like:
// b:
// if k then c else c
// c:
// v = Phi(x, y)
// Then the indexes tell you whether x is chosen from
// the if or else branch from b.
// b.Succs = [{c,0},{c,1}]
// c.Preds = [{b,0},{b,1}]
// means x is chosen if k is true.
type Edge struct {
// block edge goes to (in a Succs list) or from (in a Preds list)
b *Block
// index of reverse edge. Invariant:
// e := x.Succs[idx]
// e.b.Preds[e.i] = Edge{x,idx}
// and similarly for predecessors.
i int
}
// Storage for Succs, Preds, and Values func (e Edge) Block() *Block {
succstorage [2]*Block return e.b
predstorage [4]*Block
valstorage [8]*Value
} }
// kind control successors // kind control successors
...@@ -66,7 +96,7 @@ type Block struct { ...@@ -66,7 +96,7 @@ type Block struct {
// Plain nil [next] // Plain nil [next]
// If a boolean Value [then, else] // If a boolean Value [then, else]
// Call mem [nopanic, panic] (control opcode should be OpCall or OpStaticCall) // Call mem [nopanic, panic] (control opcode should be OpCall or OpStaticCall)
type BlockKind int32 type BlockKind int8
// short form print // short form print
func (b *Block) String() string { func (b *Block) String() string {
...@@ -85,7 +115,7 @@ func (b *Block) LongString() string { ...@@ -85,7 +115,7 @@ func (b *Block) LongString() string {
if len(b.Succs) > 0 { if len(b.Succs) > 0 {
s += " ->" s += " ->"
for _, c := range b.Succs { for _, c := range b.Succs {
s += " " + c.String() s += " " + c.b.String()
} }
} }
switch b.Likely { switch b.Likely {
...@@ -110,8 +140,53 @@ func (b *Block) SetControl(v *Value) { ...@@ -110,8 +140,53 @@ func (b *Block) SetControl(v *Value) {
// AddEdgeTo adds an edge from block b to block c. Used during building of the // AddEdgeTo adds an edge from block b to block c. Used during building of the
// SSA graph; do not use on an already-completed SSA graph. // SSA graph; do not use on an already-completed SSA graph.
func (b *Block) AddEdgeTo(c *Block) { func (b *Block) AddEdgeTo(c *Block) {
b.Succs = append(b.Succs, c) i := len(b.Succs)
c.Preds = append(c.Preds, b) j := len(c.Preds)
b.Succs = append(b.Succs, Edge{c, j})
c.Preds = append(c.Preds, Edge{b, i})
}
// removePred removes the ith input edge from b.
// It is the responsibility of the caller to remove
// the corresponding successor edge.
func (b *Block) removePred(i int) {
n := len(b.Preds) - 1
if i != n {
e := b.Preds[n]
b.Preds[i] = e
// Update the other end of the edge we moved.
e.b.Succs[e.i].i = i
}
b.Preds[n] = Edge{}
b.Preds = b.Preds[:n]
}
// removeSucc removes the ith output edge from b.
// It is the responsibility of the caller to remove
// the corresponding predecessor edge.
func (b *Block) removeSucc(i int) {
n := len(b.Succs) - 1
if i != n {
e := b.Succs[n]
b.Succs[i] = e
// Update the other end of the edge we moved.
e.b.Preds[e.i].i = i
}
b.Succs[n] = Edge{}
b.Succs = b.Succs[:n]
}
func (b *Block) swapSuccessors() {
if len(b.Succs) != 2 {
b.Fatalf("swapSuccessors with len(Succs)=%d", len(b.Succs))
}
e0 := b.Succs[0]
e1 := b.Succs[1]
b.Succs[0] = e1
b.Succs[1] = e0
e0.b.Preds[e0.i].i = 1
e1.b.Preds[e1.i].i = 0
b.Likely *= -1
} }
func (b *Block) Logf(msg string, args ...interface{}) { b.Func.Logf(msg, args...) } func (b *Block) Logf(msg string, args ...interface{}) { b.Func.Logf(msg, args...) }
......
...@@ -18,36 +18,14 @@ func checkFunc(f *Func) { ...@@ -18,36 +18,14 @@ func checkFunc(f *Func) {
f.Fatalf("%s.Func=%s, want %s", b, b.Func.Name, f.Name) f.Fatalf("%s.Func=%s, want %s", b, b.Func.Name, f.Name)
} }
if f.RegAlloc == nil { for i, e := range b.Preds {
for i, c := range b.Succs { if se := e.b.Succs[e.i]; se.b != b || se.i != i {
for j, d := range b.Succs { f.Fatalf("block pred/succ not crosslinked correctly %d:%s %d:%s", i, b, se.i, se.b)
if i != j && c == d {
f.Fatalf("%s.Succs has duplicate block %s", b, c)
}
}
}
}
// Note: duplicate successors are hard in the following case:
// if(...) goto x else goto x
// x: v = phi(a, b)
// If the conditional is true, does v get the value of a or b?
// We could solve this other ways, but the easiest is just to
// require (by possibly adding empty control-flow blocks) that
// all successors are distinct. They will need to be distinct
// anyway for register allocation (duplicate successors implies
// the existence of critical edges).
// After regalloc we can allow non-distinct predecessors.
for _, p := range b.Preds {
var found bool
for _, c := range p.Succs {
if c == b {
found = true
break
} }
} }
if !found { for i, e := range b.Succs {
f.Fatalf("block %s is not a succ of its pred block %s", b, p) if pe := e.b.Preds[e.i]; pe.b != b || pe.i != i {
f.Fatalf("block succ/pred not crosslinked correctly %d:%s %d:%s", i, b, pe.i, pe.b)
} }
} }
...@@ -252,12 +230,12 @@ func checkFunc(f *Func) { ...@@ -252,12 +230,12 @@ func checkFunc(f *Func) {
} }
for _, b := range f.Blocks { for _, b := range f.Blocks {
for _, c := range b.Preds { for _, c := range b.Preds {
if !blockMark[c.ID] { if !blockMark[c.b.ID] {
f.Fatalf("predecessor block %v for %v is missing", c, b) f.Fatalf("predecessor block %v for %v is missing", c, b)
} }
} }
for _, c := range b.Succs { for _, c := range b.Succs {
if !blockMark[c.ID] { if !blockMark[c.b.ID] {
f.Fatalf("successor block %v for %v is missing", c, b) f.Fatalf("successor block %v for %v is missing", c, b)
} }
} }
...@@ -280,7 +258,7 @@ func checkFunc(f *Func) { ...@@ -280,7 +258,7 @@ func checkFunc(f *Func) {
f.Fatalf("control value for %s is missing: %v", b, b.Control) f.Fatalf("control value for %s is missing: %v", b, b.Control)
} }
} }
for b := f.freeBlocks; b != nil; b = b.succstorage[0] { for b := f.freeBlocks; b != nil; b = b.succstorage[0].b {
if blockMark[b.ID] { if blockMark[b.ID] {
f.Fatalf("used block b%d in free list", b.ID) f.Fatalf("used block b%d in free list", b.ID)
} }
...@@ -303,7 +281,7 @@ func checkFunc(f *Func) { ...@@ -303,7 +281,7 @@ func checkFunc(f *Func) {
x := arg.Block x := arg.Block
y := b y := b
if v.Op == OpPhi { if v.Op == OpPhi {
y = b.Preds[i] y = b.Preds[i].b
} }
if !domCheck(f, sdom, x, y) { if !domCheck(f, sdom, x, y) {
f.Fatalf("arg %d of value %s does not dominate, arg=%s", i, v.LongString(), arg.LongString()) f.Fatalf("arg %d of value %s does not dominate, arg=%s", i, v.LongString(), arg.LongString())
......
...@@ -40,9 +40,12 @@ func critical(f *Func) { ...@@ -40,9 +40,12 @@ func critical(f *Func) {
} }
// split input edges coming from multi-output blocks. // split input edges coming from multi-output blocks.
for i := 0; i < len(b.Preds); i++ { for i := 0; i < len(b.Preds); {
c := b.Preds[i] e := b.Preds[i]
if c.Kind == BlockPlain { p := e.b
pi := e.i
if p.Kind == BlockPlain {
i++
continue // only single output block continue // only single output block
} }
...@@ -57,10 +60,10 @@ func critical(f *Func) { ...@@ -57,10 +60,10 @@ func critical(f *Func) {
// since we're iterating over len(f.Blocks) above, this forces // since we're iterating over len(f.Blocks) above, this forces
// the new blocks to be re-examined. // the new blocks to be re-examined.
d = f.NewBlock(BlockPlain) d = f.NewBlock(BlockPlain)
d.Line = c.Line d.Line = p.Line
blocks[argID] = d blocks[argID] = d
if f.pass.debug > 0 { if f.pass.debug > 0 {
f.Config.Warnl(c.Line, "split critical edge") f.Config.Warnl(p.Line, "split critical edge")
} }
} else { } else {
reusedBlock = true reusedBlock = true
...@@ -69,9 +72,9 @@ func critical(f *Func) { ...@@ -69,9 +72,9 @@ func critical(f *Func) {
// no existing block, so allocate a new block // no existing block, so allocate a new block
// to place on the edge // to place on the edge
d = f.NewBlock(BlockPlain) d = f.NewBlock(BlockPlain)
d.Line = c.Line d.Line = p.Line
if f.pass.debug > 0 { if f.pass.debug > 0 {
f.Config.Warnl(c.Line, "split critical edge") f.Config.Warnl(p.Line, "split critical edge")
} }
} }
...@@ -80,57 +83,34 @@ func critical(f *Func) { ...@@ -80,57 +83,34 @@ func critical(f *Func) {
// corresponding elements from the block // corresponding elements from the block
// predecessors and phi args // predecessors and phi args
if reusedBlock { if reusedBlock {
d.Preds = append(d.Preds, c) // Add p->d edge
b.Preds[i] = nil p.Succs[pi] = Edge{d, len(d.Preds)}
phi.Args[i].Uses-- d.Preds = append(d.Preds, Edge{p, pi})
phi.Args[i] = nil
} else {
// 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. // Remove p as a predecessor from b.
for j, b2 := range c.Succs { b.removePred(i)
if b2 == b {
c.Succs[j] = d
break
}
}
}
// clean up phi's args and b's predecessor list // Update corresponding phi args
if phi != nil { n := len(b.Preds)
phi.Args = filterNilValues(phi.Args) phi.Args[i].Uses--
b.Preds = filterNilBlocks(b.Preds) phi.Args[i] = phi.Args[n]
phi.Args[n] = nil
phi.Args = phi.Args[:n]
// splitting occasionally leads to a phi having // splitting occasionally leads to a phi having
// a single argument (occurs with -N) // a single argument (occurs with -N)
if len(phi.Args) == 1 { if n == 1 {
phi.Op = OpCopy 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++
} }
} }
}
// 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
} }
...@@ -25,7 +25,8 @@ func reachableBlocks(f *Func) []bool { ...@@ -25,7 +25,8 @@ func reachableBlocks(f *Func) []bool {
if b.Kind == BlockFirst { if b.Kind == BlockFirst {
s = s[:1] s = s[:1]
} }
for _, c := range s { for _, e := range s {
c := e.b
if !reachable[c.ID] { if !reachable[c.ID] {
reachable[c.ID] = true reachable[c.ID] = true
p = append(p, c) // push p = append(p, c) // push
...@@ -69,7 +70,7 @@ func liveValues(f *Func, reachable []bool) []bool { ...@@ -69,7 +70,7 @@ func liveValues(f *Func, reachable []bool) []bool {
v := q[len(q)-1] v := q[len(q)-1]
q = q[:len(q)-1] q = q[:len(q)-1]
for i, x := range v.Args { for i, x := range v.Args {
if v.Op == OpPhi && !reachable[v.Block.Preds[i].ID] { if v.Op == OpPhi && !reachable[v.Block.Preds[i].b.ID] {
continue continue
} }
if !live[x.ID] { if !live[x.ID] {
...@@ -100,9 +101,12 @@ func deadcode(f *Func) { ...@@ -100,9 +101,12 @@ func deadcode(f *Func) {
if reachable[b.ID] { if reachable[b.ID] {
continue continue
} }
for _, c := range b.Succs { for i := 0; i < len(b.Succs); {
if reachable[c.ID] { e := b.Succs[i]
c.removePred(b) if reachable[e.b.ID] {
b.removeEdge(i)
} else {
i++
} }
} }
} }
...@@ -115,16 +119,9 @@ func deadcode(f *Func) { ...@@ -115,16 +119,9 @@ func deadcode(f *Func) {
if b.Kind != BlockFirst { if b.Kind != BlockFirst {
continue continue
} }
c := b.Succs[1] b.removeEdge(1)
b.Succs[1] = nil
b.Succs = b.Succs[:1]
b.Kind = BlockPlain b.Kind = BlockPlain
b.Likely = BranchUnknown b.Likely = BranchUnknown
if reachable[c.ID] {
// Note: c must be reachable through some other edge.
c.removePred(b)
}
} }
// Splice out any copies introduced during dead block removal. // Splice out any copies introduced during dead block removal.
...@@ -217,35 +214,28 @@ func deadcode(f *Func) { ...@@ -217,35 +214,28 @@ func deadcode(f *Func) {
f.Blocks = f.Blocks[:i] f.Blocks = f.Blocks[:i]
} }
// removePred removes the predecessor p from b's predecessor list. // removeEdge removes the i'th outgoing edge from b (and
func (b *Block) removePred(p *Block) { // the corresponding incoming edge from b.Succs[i].b).
var i int func (b *Block) removeEdge(i int) {
found := false e := b.Succs[i]
for j, q := range b.Preds { c := e.b
if q == p { j := e.i
i = j
found = true
break
}
}
// TODO: the above loop could make the deadcode pass take quadratic time
if !found {
b.Fatalf("can't find predecessor %v of %v\n", p, b)
}
n := len(b.Preds) - 1 // Adjust b.Succs
b.Preds[i] = b.Preds[n] b.removeSucc(i)
b.Preds[n] = nil // aid GC
b.Preds = b.Preds[:n]
// rewrite phi ops to match the new predecessor list // Adjust c.Preds
for _, v := range b.Values { c.removePred(j)
// Remove phi args from c's phis.
n := len(c.Preds)
for _, v := range c.Values {
if v.Op != OpPhi { if v.Op != OpPhi {
continue continue
} }
v.Args[i].Uses-- v.Args[j].Uses--
v.Args[i] = v.Args[n] v.Args[j] = v.Args[n]
v.Args[n] = nil // aid GC v.Args[n] = nil
v.Args = v.Args[:n] v.Args = v.Args[:n]
phielimValue(v) phielimValue(v)
// Note: this is trickier than it looks. Replacing // Note: this is trickier than it looks. Replacing
......
...@@ -4,7 +4,11 @@ ...@@ -4,7 +4,11 @@
package ssa package ssa
import "testing" import (
"fmt"
"strconv"
"testing"
)
func TestDeadLoop(t *testing.T) { func TestDeadLoop(t *testing.T) {
c := testConfig(t) c := testConfig(t)
...@@ -132,3 +136,26 @@ func TestNestedDeadBlocks(t *testing.T) { ...@@ -132,3 +136,26 @@ func TestNestedDeadBlocks(t *testing.T) {
} }
} }
} }
func BenchmarkDeadCode(b *testing.B) {
for _, n := range [...]int{1, 10, 100, 1000, 10000, 100000, 200000} {
b.Run(strconv.Itoa(n), func(b *testing.B) {
c := testConfig(b)
blocks := make([]bloc, 0, n+2)
blocks = append(blocks,
Bloc("entry",
Valu("mem", OpInitMem, TypeMem, 0, nil),
Goto("exit")))
blocks = append(blocks, Bloc("exit", Exit("mem")))
for i := 0; i < n; i++ {
blocks = append(blocks, Bloc(fmt.Sprintf("dead%d", i), Goto("exit")))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
fun := Fun(c, "entry", blocks...)
Deadcode(fun.f)
fun.f.Free()
}
})
}
}
...@@ -47,7 +47,8 @@ func postorderWithNumbering(f *Func, ponums []int) []*Block { ...@@ -47,7 +47,8 @@ func postorderWithNumbering(f *Func, ponums []int) []*Block {
// Children have not been visited yet. Mark as explored // Children have not been visited yet. Mark as explored
// and queue any children we haven't seen yet. // and queue any children we haven't seen yet.
mark[b.ID] = explored mark[b.ID] = explored
for _, c := range b.Succs { for _, e := range b.Succs {
c := e.b
if mark[c.ID] == notFound { if mark[c.ID] == notFound {
mark[c.ID] = notExplored mark[c.ID] = notExplored
s = append(s, c) s = append(s, c)
...@@ -60,7 +61,7 @@ func postorderWithNumbering(f *Func, ponums []int) []*Block { ...@@ -60,7 +61,7 @@ func postorderWithNumbering(f *Func, ponums []int) []*Block {
return order return order
} }
type linkedBlocks func(*Block) []*Block type linkedBlocks func(*Block) []Edge
const nscratchslices = 7 const nscratchslices = 7
...@@ -101,8 +102,8 @@ func (cfg *Config) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g []ID ...@@ -101,8 +102,8 @@ func (cfg *Config) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g []ID
} }
func dominators(f *Func) []*Block { func dominators(f *Func) []*Block {
preds := func(b *Block) []*Block { return b.Preds } preds := func(b *Block) []Edge { return b.Preds }
succs := func(b *Block) []*Block { return b.Succs } succs := func(b *Block) []Edge { return b.Succs }
//TODO: benchmark and try to find criteria for swapping between //TODO: benchmark and try to find criteria for swapping between
// dominatorsSimple and dominatorsLT // dominatorsSimple and dominatorsLT
...@@ -135,7 +136,8 @@ func (f *Func) dominatorsLTOrig(entry *Block, predFn linkedBlocks, succFn linked ...@@ -135,7 +136,8 @@ func (f *Func) dominatorsLTOrig(entry *Block, predFn linkedBlocks, succFn linked
w := vertex[i] w := vertex[i]
// step2 in TOPLAS paper // step2 in TOPLAS paper
for _, v := range predFn(fromID[w]) { for _, e := range predFn(fromID[w]) {
v := e.b
if semi[v.ID] == 0 { if semi[v.ID] == 0 {
// skip unreachable predecessor // skip unreachable predecessor
// not in original, but we're using existing pred instead of building one. // not in original, but we're using existing pred instead of building one.
...@@ -199,7 +201,8 @@ func (f *Func) dfsOrig(entry *Block, succFn linkedBlocks, semi, vertex, label, p ...@@ -199,7 +201,8 @@ func (f *Func) dfsOrig(entry *Block, succFn linkedBlocks, semi, vertex, label, p
vertex[n] = v.ID vertex[n] = v.ID
label[v.ID] = v.ID label[v.ID] = v.ID
// ancestor[v] already zero // ancestor[v] already zero
for _, w := range succFn(v) { for _, e := range succFn(v) {
w := e.b
// if it has a dfnum, we've already visited it // if it has a dfnum, we've already visited it
if semi[w.ID] == 0 { if semi[w.ID] == 0 {
// yes, w can be pushed multiple times. // yes, w can be pushed multiple times.
...@@ -265,7 +268,8 @@ func dominatorsSimple(f *Func) []*Block { ...@@ -265,7 +268,8 @@ func dominatorsSimple(f *Func) []*Block {
for i := len(post) - 2; i >= 0; i-- { for i := len(post) - 2; i >= 0; i-- {
b := post[i] b := post[i]
var d *Block var d *Block
for _, p := range b.Preds { for _, e := range b.Preds {
p := e.b
if idom[p.ID] == nil { if idom[p.ID] == nil {
continue continue
} }
......
...@@ -43,7 +43,8 @@ func flagalloc(f *Func) { ...@@ -43,7 +43,8 @@ func flagalloc(f *Func) {
} }
} }
if flag != nil { if flag != nil {
for _, p := range b.Preds { for _, e := range b.Preds {
p := e.b
end[p.ID] = flag end[p.ID] = flag
} }
} }
...@@ -73,9 +74,10 @@ func flagalloc(f *Func) { ...@@ -73,9 +74,10 @@ func flagalloc(f *Func) {
// The current live flag value the pre-flagalloc copy). // The current live flag value the pre-flagalloc copy).
var flag *Value var flag *Value
if len(b.Preds) > 0 { if len(b.Preds) > 0 {
flag = end[b.Preds[0].ID] flag = end[b.Preds[0].b.ID]
// Note: the following condition depends on the lack of critical edges. // Note: the following condition depends on the lack of critical edges.
for _, p := range b.Preds[1:] { for _, e := range b.Preds[1:] {
p := e.b
if end[p.ID] != flag { if end[p.ID] != flag {
f.Fatalf("live flag in %s's predecessors not consistent", b) f.Fatalf("live flag in %s's predecessors not consistent", b)
} }
......
...@@ -34,7 +34,7 @@ type Func struct { ...@@ -34,7 +34,7 @@ type Func struct {
Names []LocalSlot Names []LocalSlot
freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil. freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil.
freeBlocks *Block // free Blocks linked by succstorage[0]. All other fields except ID are 0/nil. freeBlocks *Block // free Blocks linked by succstorage[0].b. All other fields except ID are 0/nil.
idom []*Block // precomputed immediate dominators idom []*Block // precomputed immediate dominators
sdom sparseTree // precomputed dominator tree sdom sparseTree // precomputed dominator tree
...@@ -146,8 +146,8 @@ func (f *Func) NewBlock(kind BlockKind) *Block { ...@@ -146,8 +146,8 @@ func (f *Func) NewBlock(kind BlockKind) *Block {
var b *Block var b *Block
if f.freeBlocks != nil { if f.freeBlocks != nil {
b = f.freeBlocks b = f.freeBlocks
f.freeBlocks = b.succstorage[0] f.freeBlocks = b.succstorage[0].b
b.succstorage[0] = nil b.succstorage[0].b = nil
} else { } else {
ID := f.bid.get() ID := f.bid.get()
if int(ID) < len(f.Config.blocks) { if int(ID) < len(f.Config.blocks) {
...@@ -173,7 +173,7 @@ func (f *Func) freeBlock(b *Block) { ...@@ -173,7 +173,7 @@ func (f *Func) freeBlock(b *Block) {
id := b.ID id := b.ID
*b = Block{} *b = Block{}
b.ID = id b.ID = id
b.succstorage[0] = f.freeBlocks b.succstorage[0].b = f.freeBlocks
f.freeBlocks = b f.freeBlocks = b
} }
......
...@@ -104,7 +104,7 @@ func Equiv(f, g *Func) bool { ...@@ -104,7 +104,7 @@ func Equiv(f, g *Func) bool {
return false return false
} }
for i := range fb.Succs { for i := range fb.Succs {
if !checkBlk(fb.Succs[i], gb.Succs[i]) { if !checkBlk(fb.Succs[i].b, gb.Succs[i].b) {
return false return false
} }
} }
...@@ -112,7 +112,7 @@ func Equiv(f, g *Func) bool { ...@@ -112,7 +112,7 @@ func Equiv(f, g *Func) bool {
return false return false
} }
for i := range fb.Preds { for i := range fb.Preds {
if !checkBlk(fb.Preds[i], gb.Preds[i]) { if !checkBlk(fb.Preds[i].b, gb.Preds[i].b) {
return false return false
} }
} }
......
...@@ -25,6 +25,11 @@ func fuse(f *Func) { ...@@ -25,6 +25,11 @@ func fuse(f *Func) {
// //
// If all Phi ops in ss have identical variables for slots corresponding to // If all Phi ops in ss have identical variables for slots corresponding to
// s0, s1 and b then the branch can be dropped. // s0, s1 and b then the branch can be dropped.
// This optimization often comes up in switch statements with multiple
// expressions in a case clause:
// switch n {
// case 1,2,3: return 4
// }
// TODO: If ss doesn't contain any OpPhis, are s0 and s1 dead code anyway. // TODO: If ss doesn't contain any OpPhis, are s0 and s1 dead code anyway.
func fuseBlockIf(b *Block) bool { func fuseBlockIf(b *Block) bool {
if b.Kind != BlockIf { if b.Kind != BlockIf {
...@@ -32,17 +37,21 @@ func fuseBlockIf(b *Block) bool { ...@@ -32,17 +37,21 @@ func fuseBlockIf(b *Block) bool {
} }
var ss0, ss1 *Block var ss0, ss1 *Block
s0 := b.Succs[0] s0 := b.Succs[0].b
i0 := b.Succs[0].i
if s0.Kind != BlockPlain || len(s0.Preds) != 1 || len(s0.Values) != 0 { if s0.Kind != BlockPlain || len(s0.Preds) != 1 || len(s0.Values) != 0 {
s0, ss0 = b, s0 s0, ss0 = b, s0
} else { } else {
ss0 = s0.Succs[0] ss0 = s0.Succs[0].b
i0 = s0.Succs[0].i
} }
s1 := b.Succs[1] s1 := b.Succs[1].b
i1 := b.Succs[1].i
if s1.Kind != BlockPlain || len(s1.Preds) != 1 || len(s1.Values) != 0 { if s1.Kind != BlockPlain || len(s1.Preds) != 1 || len(s1.Values) != 0 {
s1, ss1 = b, s1 s1, ss1 = b, s1
} else { } else {
ss1 = s1.Succs[0] ss1 = s1.Succs[0].b
i1 = s1.Succs[0].i
} }
if ss0 != ss1 { if ss0 != ss1 {
...@@ -52,18 +61,7 @@ func fuseBlockIf(b *Block) bool { ...@@ -52,18 +61,7 @@ func fuseBlockIf(b *Block) bool {
// s0 and s1 are equal with b if the corresponding block is missing // s0 and s1 are equal with b if the corresponding block is missing
// (2nd, 3rd and 4th case in the figure). // (2nd, 3rd and 4th case in the figure).
i0, i1 := -1, -1
for i, p := range ss.Preds {
if p == s0 {
i0 = i
}
if p == s1 {
i1 = i
}
}
if i0 == -1 || i1 == -1 {
b.Fatalf("invalid predecessors")
}
for _, v := range ss.Values { for _, v := range ss.Values {
if v.Op == OpPhi && v.Uses > 0 && v.Args[i0] != v.Args[i1] { if v.Op == OpPhi && v.Uses > 0 && v.Args[i0] != v.Args[i1] {
return false return false
...@@ -76,28 +74,23 @@ func fuseBlockIf(b *Block) bool { ...@@ -76,28 +74,23 @@ func fuseBlockIf(b *Block) bool {
// have identical predecessors (verified above). // have identical predecessors (verified above).
// No critical edge is introduced because b will have one successor. // No critical edge is introduced because b will have one successor.
if s0 != b && s1 != b { if s0 != b && s1 != b {
ss.removePred(s0) // Replace edge b->s0->ss with b->ss.
// Replace edge b->s1->ss with b->ss.
// We need to keep a slot for Phis corresponding to b. // We need to keep a slot for Phis corresponding to b.
for i := range b.Succs { b.Succs[0] = Edge{ss, i0}
if b.Succs[i] == s1 { ss.Preds[i0] = Edge{b, 0}
b.Succs[i] = ss b.removeEdge(1)
} s1.removeEdge(0)
}
for i := range ss.Preds {
if ss.Preds[i] == s1 {
ss.Preds[i] = b
}
}
} else if s0 != b { } else if s0 != b {
ss.removePred(s0) b.removeEdge(0)
s0.removeEdge(0)
} else if s1 != b { } else if s1 != b {
ss.removePred(s1) b.removeEdge(1)
s1.removeEdge(0)
} else {
b.removeEdge(1)
} }
b.Kind = BlockPlain b.Kind = BlockPlain
b.SetControl(nil) b.SetControl(nil)
b.Succs = append(b.Succs[:0], ss)
// Trash the empty blocks s0 & s1. // Trash the empty blocks s0 & s1.
if s0 != b { if s0 != b {
...@@ -120,30 +113,27 @@ func fuseBlockPlain(b *Block) bool { ...@@ -120,30 +113,27 @@ func fuseBlockPlain(b *Block) bool {
return false return false
} }
c := b.Succs[0] c := b.Succs[0].b
if len(c.Preds) != 1 { if len(c.Preds) != 1 {
return false return false
} }
// move all of b'c values to c. // move all of b's values to c.
for _, v := range b.Values { for _, v := range b.Values {
v.Block = c v.Block = c
c.Values = append(c.Values, v) c.Values = append(c.Values, v)
} }
// replace b->c edge with preds(b) -> c // replace b->c edge with preds(b) -> c
c.predstorage[0] = nil c.predstorage[0] = Edge{}
if len(b.Preds) > len(b.predstorage) { if len(b.Preds) > len(b.predstorage) {
c.Preds = b.Preds c.Preds = b.Preds
} else { } else {
c.Preds = append(c.predstorage[:0], b.Preds...) c.Preds = append(c.predstorage[:0], b.Preds...)
} }
for _, p := range c.Preds { for i, e := range c.Preds {
for i, q := range p.Succs { p := e.b
if q == b { p.Succs[e.i] = Edge{c, i}
p.Succs[i] = c
}
}
} }
if f := b.Func; f.Entry == b { if f := b.Func; f.Entry == b {
f.Entry = c f.Entry = c
......
package ssa package ssa
import ( import (
"fmt"
"strconv"
"testing" "testing"
) )
...@@ -127,3 +129,41 @@ func TestFuseEliminatesEmptyBlocks(t *testing.T) { ...@@ -127,3 +129,41 @@ func TestFuseEliminatesEmptyBlocks(t *testing.T) {
} }
} }
} }
func BenchmarkFuse(b *testing.B) {
for _, n := range [...]int{1, 10, 100, 1000, 10000} {
b.Run(strconv.Itoa(n), func(b *testing.B) {
c := testConfig(b)
blocks := make([]bloc, 0, 2*n+3)
blocks = append(blocks,
Bloc("entry",
Valu("mem", OpInitMem, TypeMem, 0, nil),
Valu("cond", OpArg, TypeBool, 0, nil),
Valu("x", OpArg, TypeInt64, 0, nil),
Goto("exit")))
phiArgs := make([]string, 0, 2*n)
for i := 0; i < n; i++ {
cname := fmt.Sprintf("c%d", i)
blocks = append(blocks,
Bloc(fmt.Sprintf("b%d", i), If("cond", cname, "merge")),
Bloc(cname, Goto("merge")))
phiArgs = append(phiArgs, "x", "x")
}
blocks = append(blocks,
Bloc("merge",
Valu("phi", OpPhi, TypeMem, 0, nil, phiArgs...),
Goto("exit")),
Bloc("exit",
Exit("mem")))
b.ResetTimer()
for i := 0; i < b.N; i++ {
fun := Fun(c, "entry", blocks...)
fuse(fun.f)
fun.f.Free()
}
})
}
}
...@@ -273,35 +273,30 @@ func genRules(arch arch) { ...@@ -273,35 +273,30 @@ func genRules(arch arch) {
log.Fatalf("unmatched successors %v in %s", m, rule) log.Fatalf("unmatched successors %v in %s", m, rule)
} }
// Modify predecessor lists for no-longer-reachable blocks
for succ := range m {
fmt.Fprintf(w, "b.Func.removePredecessor(b, %s)\n", succ)
}
fmt.Fprintf(w, "b.Kind = %s\n", blockName(t[0], arch)) fmt.Fprintf(w, "b.Kind = %s\n", blockName(t[0], arch))
if t[1] == "nil" { if t[1] == "nil" {
fmt.Fprintf(w, "b.SetControl(nil)\n") fmt.Fprintf(w, "b.SetControl(nil)\n")
} else { } else {
fmt.Fprintf(w, "b.SetControl(%s)\n", genResult0(w, arch, t[1], new(int), false, false, rule.loc)) fmt.Fprintf(w, "b.SetControl(%s)\n", genResult0(w, arch, t[1], new(int), false, false, rule.loc))
} }
if len(newsuccs) < len(succs) {
fmt.Fprintf(w, "b.Succs = b.Succs[:%d]\n", len(newsuccs)) succChanged := false
for i := 0; i < len(succs); i++ {
if succs[i] != newsuccs[i] {
succChanged = true
} }
for i, a := range newsuccs {
fmt.Fprintf(w, "b.Succs[%d] = %s\n", i, a)
} }
// Update branch prediction if succChanged {
switch { if len(succs) != 2 {
case len(newsuccs) != 2: log.Fatalf("changed successors, len!=2 in %s", rule)
fmt.Fprintln(w, "b.Likely = BranchUnknown") }
case newsuccs[0] == succs[0] && newsuccs[1] == succs[1]: if succs[0] != newsuccs[1] || succs[1] != newsuccs[0] {
// unchanged log.Fatalf("can only handle swapped successors in %s", rule)
case newsuccs[0] == succs[1] && newsuccs[1] == succs[0]: }
// flipped fmt.Fprintln(w, "b.swapSuccessors()")
fmt.Fprintln(w, "b.Likely *= -1") }
default: for i := 0; i < len(succs); i++ {
// unknown fmt.Fprintf(w, "_ = %s\n", newsuccs[i])
fmt.Fprintln(w, "b.Likely = BranchUnknown")
} }
if *genLog { if *genLog {
......
...@@ -383,7 +383,8 @@ func (b *Block) LongHTML() string { ...@@ -383,7 +383,8 @@ func (b *Block) LongHTML() string {
} }
if len(b.Succs) > 0 { if len(b.Succs) > 0 {
s += " &#8594;" // right arrow s += " &#8594;" // right arrow
for _, c := range b.Succs { for _, e := range b.Succs {
c := e.b
s += " " + c.HTML() s += " " + c.HTML()
} }
} }
...@@ -423,7 +424,8 @@ func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) { ...@@ -423,7 +424,8 @@ func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) {
fmt.Fprintf(p.w, "<li class=\"ssa-start-block\">%s:", b.HTML()) fmt.Fprintf(p.w, "<li class=\"ssa-start-block\">%s:", b.HTML())
if len(b.Preds) > 0 { if len(b.Preds) > 0 {
io.WriteString(p.w, " &#8592;") // left arrow io.WriteString(p.w, " &#8592;") // left arrow
for _, pred := range b.Preds { for _, e := range b.Preds {
pred := e.b
fmt.Fprintf(p.w, " %s", pred.HTML()) fmt.Fprintf(p.w, " %s", pred.HTML())
} }
} }
......
...@@ -39,7 +39,8 @@ blockloop: ...@@ -39,7 +39,8 @@ blockloop:
break break
} }
for _, c := range b.Succs { for _, e := range b.Succs {
c := e.b
indegree[c.ID]-- indegree[c.ID]--
if indegree[c.ID] == 0 { if indegree[c.ID] == 0 {
posdegree.remove(c.ID) posdegree.remove(c.ID)
...@@ -54,9 +55,9 @@ blockloop: ...@@ -54,9 +55,9 @@ blockloop:
var likely *Block var likely *Block
switch b.Likely { switch b.Likely {
case BranchLikely: case BranchLikely:
likely = b.Succs[0] likely = b.Succs[0].b
case BranchUnlikely: case BranchUnlikely:
likely = b.Succs[1] likely = b.Succs[1].b
} }
if likely != nil && !scheduled[likely.ID] { if likely != nil && !scheduled[likely.ID] {
bid = likely.ID bid = likely.ID
...@@ -66,7 +67,8 @@ blockloop: ...@@ -66,7 +67,8 @@ blockloop:
// Use degree for now. // Use degree for now.
bid = 0 bid = 0
mindegree := f.NumBlocks() mindegree := f.NumBlocks()
for _, c := range order[len(order)-1].Succs { for _, e := range order[len(order)-1].Succs {
c := e.b
if scheduled[c.ID] { if scheduled[c.ID] {
continue continue
} }
......
...@@ -134,11 +134,11 @@ func likelyadjust(f *Func) { ...@@ -134,11 +134,11 @@ func likelyadjust(f *Func) {
// and less influential than inferences from loop structure. // and less influential than inferences from loop structure.
case BlockCall, BlockDefer: case BlockCall, BlockDefer:
local[b.ID] = blCALL local[b.ID] = blCALL
certain[b.ID] = max8(blCALL, certain[b.Succs[0].ID]) certain[b.ID] = max8(blCALL, certain[b.Succs[0].b.ID])
default: default:
if len(b.Succs) == 1 { if len(b.Succs) == 1 {
certain[b.ID] = certain[b.Succs[0].ID] certain[b.ID] = certain[b.Succs[0].b.ID]
} else if len(b.Succs) == 2 { } else if len(b.Succs) == 2 {
// If successor is an unvisited backedge, it's in loop and we don't care. // If successor is an unvisited backedge, it's in loop and we don't care.
// Its default unlikely is also zero which is consistent with favoring loop edges. // Its default unlikely is also zero which is consistent with favoring loop edges.
...@@ -146,8 +146,8 @@ func likelyadjust(f *Func) { ...@@ -146,8 +146,8 @@ func likelyadjust(f *Func) {
// default "everything returns" unlikeliness is erased by min with the // default "everything returns" unlikeliness is erased by min with the
// backedge likeliness; however a loop with calls on every path will be // backedge likeliness; however a loop with calls on every path will be
// tagged with call cost. Net effect is that loop entry is favored. // tagged with call cost. Net effect is that loop entry is favored.
b0 := b.Succs[0].ID b0 := b.Succs[0].b.ID
b1 := b.Succs[1].ID b1 := b.Succs[1].b.ID
certain[b.ID] = min8(certain[b0], certain[b1]) certain[b.ID] = min8(certain[b0], certain[b1])
l := b2l[b.ID] l := b2l[b.ID]
...@@ -270,7 +270,8 @@ func loopnestfor(f *Func) *loopnest { ...@@ -270,7 +270,8 @@ func loopnestfor(f *Func) *loopnest {
// and there may be more than one such s. // and there may be more than one such s.
// Since there's at most 2 successors, the inner/outer ordering // Since there's at most 2 successors, the inner/outer ordering
// between them can be established with simple comparisons. // between them can be established with simple comparisons.
for _, bb := range b.Succs { for _, e := range b.Succs {
bb := e.b
l := b2l[bb.ID] l := b2l[bb.ID]
if sdom.isAncestorEq(bb, b) { // Found a loop header if sdom.isAncestorEq(bb, b) { // Found a loop header
...@@ -405,12 +406,12 @@ func (ln *loopnest) findExits() { ...@@ -405,12 +406,12 @@ func (ln *loopnest) findExits() {
for _, b := range ln.po { for _, b := range ln.po {
l := b2l[b.ID] l := b2l[b.ID]
if l != nil && len(b.Succs) == 2 { if l != nil && len(b.Succs) == 2 {
sl := b2l[b.Succs[0].ID] sl := b2l[b.Succs[0].b.ID]
if recordIfExit(l, sl, b.Succs[0]) { if recordIfExit(l, sl, b.Succs[0].b) {
continue continue
} }
sl = b2l[b.Succs[1].ID] sl = b2l[b.Succs[1].b.ID]
if recordIfExit(l, sl, b.Succs[1]) { if recordIfExit(l, sl, b.Succs[1].b) {
continue continue
} }
} }
......
...@@ -103,14 +103,14 @@ nextb: ...@@ -103,14 +103,14 @@ nextb:
// First condition: loop entry has a single predecessor, which // First condition: loop entry has a single predecessor, which
// is the header block. This implies that b.Succs[entry] is // is the header block. This implies that b.Succs[entry] is
// reached iff ind < max. // reached iff ind < max.
if len(b.Succs[entry].Preds) != 1 { if len(b.Succs[entry].b.Preds) != 1 {
// b.Succs[1-entry] must exit the loop. // b.Succs[1-entry] must exit the loop.
continue continue
} }
// Second condition: b.Succs[entry] dominates nxt so that // Second condition: b.Succs[entry] dominates nxt so that
// nxt is computed when inc < max, meaning nxt <= max. // nxt is computed when inc < max, meaning nxt <= max.
if !f.sdom.isAncestorEq(b.Succs[entry], nxt.Block) { if !f.sdom.isAncestorEq(b.Succs[entry].b, nxt.Block) {
// inc+ind can only be reached through the branch that enters the loop. // inc+ind can only be reached through the branch that enters the loop.
continue continue
} }
...@@ -150,7 +150,7 @@ nextb: ...@@ -150,7 +150,7 @@ nextb:
nxt: nxt, nxt: nxt,
min: min, min: min,
max: max, max: max,
entry: b.Succs[entry], entry: b.Succs[entry].b,
}) })
b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max) b.Logf("found induction variable %v (inc = %v, min = %v, max = %v)\n", ind, inc, min, max)
} }
......
...@@ -149,11 +149,11 @@ func checkedptr(b *Block) *Value { ...@@ -149,11 +149,11 @@ func checkedptr(b *Block) *Value {
// predecessor. // predecessor.
func nonnilptr(b *Block) *Value { func nonnilptr(b *Block) *Value {
if len(b.Preds) == 1 { if len(b.Preds) == 1 {
bp := b.Preds[0] bp := b.Preds[0].b
if bp.Kind == BlockCheck { if bp.Kind == BlockCheck {
return bp.Control.Args[0] return bp.Control.Args[0]
} }
if bp.Kind == BlockIf && bp.Control.Op == OpIsNonNil && bp.Succs[0] == b { if bp.Kind == BlockIf && bp.Control.Op == OpIsNonNil && bp.Succs[0].b == b {
return bp.Control.Args[0] return bp.Control.Args[0]
} }
} }
......
...@@ -31,6 +31,7 @@ func phielim(f *Func) { ...@@ -31,6 +31,7 @@ func phielim(f *Func) {
} }
} }
// phielimValue tries to convert the phi v to a copy.
func phielimValue(v *Value) bool { func phielimValue(v *Value) bool {
if v.Op != OpPhi { if v.Op != OpPhi {
return false return false
......
...@@ -30,16 +30,16 @@ func phiopt(f *Func) { ...@@ -30,16 +30,16 @@ func phiopt(f *Func) {
continue continue
} }
pb0, b0 := b, b.Preds[0] pb0, b0 := b, b.Preds[0].b
for len(b0.Succs) == 1 && len(b0.Preds) == 1 { for len(b0.Succs) == 1 && len(b0.Preds) == 1 {
pb0, b0 = b0, b0.Preds[0] pb0, b0 = b0, b0.Preds[0].b
} }
if b0.Kind != BlockIf { if b0.Kind != BlockIf {
continue continue
} }
pb1, b1 := b, b.Preds[1] pb1, b1 := b, b.Preds[1].b
for len(b1.Succs) == 1 && len(b1.Preds) == 1 { for len(b1.Succs) == 1 && len(b1.Preds) == 1 {
pb1, b1 = b1, b1.Preds[0] pb1, b1 = b1, b1.Preds[0].b
} }
if b1 != b0 { if b1 != b0 {
continue continue
...@@ -48,9 +48,9 @@ func phiopt(f *Func) { ...@@ -48,9 +48,9 @@ func phiopt(f *Func) {
// reverse is the predecessor from which the truth value comes. // reverse is the predecessor from which the truth value comes.
var reverse int var reverse int
if b0.Succs[0] == pb0 && b0.Succs[1] == pb1 { if b0.Succs[0].b == pb0 && b0.Succs[1].b == pb1 {
reverse = 0 reverse = 0
} else if b0.Succs[0] == pb1 && b0.Succs[1] == pb0 { } else if b0.Succs[0].b == pb1 && b0.Succs[1].b == pb0 {
reverse = 1 reverse = 1
} else { } else {
b.Fatalf("invalid predecessors\n") b.Fatalf("invalid predecessors\n")
......
...@@ -45,7 +45,8 @@ func (p stringFuncPrinter) startBlock(b *Block, reachable bool) { ...@@ -45,7 +45,8 @@ func (p stringFuncPrinter) startBlock(b *Block, reachable bool) {
fmt.Fprintf(p.w, " b%d:", b.ID) fmt.Fprintf(p.w, " b%d:", b.ID)
if len(b.Preds) > 0 { if len(b.Preds) > 0 {
io.WriteString(p.w, " <-") io.WriteString(p.w, " <-")
for _, pred := range b.Preds { for _, e := range b.Preds {
pred := e.b
fmt.Fprintf(p.w, " b%d", pred.ID) fmt.Fprintf(p.w, " b%d", pred.ID)
} }
} }
......
...@@ -502,7 +502,7 @@ func prove(f *Func) { ...@@ -502,7 +502,7 @@ func prove(f *Func) {
b.Kind = BlockFirst b.Kind = BlockFirst
b.SetControl(nil) b.SetControl(nil)
if succ == negative { if succ == negative {
b.Succs[0], b.Succs[1] = b.Succs[1], b.Succs[0] b.swapSuccessors()
} }
} }
...@@ -525,10 +525,10 @@ func getBranch(sdom sparseTree, p *Block, b *Block) branch { ...@@ -525,10 +525,10 @@ func getBranch(sdom sparseTree, p *Block, b *Block) branch {
// has one predecessor then (apart from the degenerate case), // has one predecessor then (apart from the degenerate case),
// there is no path from entry that can reach b through p.Succs[1]. // there is no path from entry that can reach b through p.Succs[1].
// TODO: how about p->yes->b->yes, i.e. a loop in yes. // TODO: how about p->yes->b->yes, i.e. a loop in yes.
if sdom.isAncestorEq(p.Succs[0], b) && len(p.Succs[0].Preds) == 1 { if sdom.isAncestorEq(p.Succs[0].b, b) && len(p.Succs[0].b.Preds) == 1 {
return positive return positive
} }
if sdom.isAncestorEq(p.Succs[1], b) && len(p.Succs[1].Preds) == 1 { if sdom.isAncestorEq(p.Succs[1].b, b) && len(p.Succs[1].b.Preds) == 1 {
return negative return negative
} }
return unknown return unknown
......
...@@ -488,11 +488,12 @@ func (s *regAllocState) init(f *Func) { ...@@ -488,11 +488,12 @@ func (s *regAllocState) init(f *Func) {
s.primary = make([]int32, f.NumBlocks()) s.primary = make([]int32, f.NumBlocks())
for _, b := range f.Blocks { for _, b := range f.Blocks {
best := -1 best := -1
for i, p := range b.Preds { for i, e := range b.Preds {
p := e.b
if blockOrder[p.ID] >= blockOrder[b.ID] { if blockOrder[p.ID] >= blockOrder[b.ID] {
continue // backward edge continue // backward edge
} }
if best == -1 || blockOrder[p.ID] > blockOrder[b.Preds[best].ID] { if best == -1 || blockOrder[p.ID] > blockOrder[b.Preds[best].b.ID] {
best = i best = i
} }
} }
...@@ -706,7 +707,7 @@ func (s *regAllocState) regalloc(f *Func) { ...@@ -706,7 +707,7 @@ func (s *regAllocState) regalloc(f *Func) {
} }
} else if len(b.Preds) == 1 { } else if len(b.Preds) == 1 {
// Start regalloc state with the end state of the previous block. // Start regalloc state with the end state of the previous block.
s.setState(s.endRegs[b.Preds[0].ID]) s.setState(s.endRegs[b.Preds[0].b.ID])
if nphi > 0 { if nphi > 0 {
f.Fatalf("phis in single-predecessor block") f.Fatalf("phis in single-predecessor block")
} }
...@@ -731,7 +732,7 @@ func (s *regAllocState) regalloc(f *Func) { ...@@ -731,7 +732,7 @@ func (s *regAllocState) regalloc(f *Func) {
if idx < 0 { if idx < 0 {
f.Fatalf("block with no primary predecessor %s", b) f.Fatalf("block with no primary predecessor %s", b)
} }
p := b.Preds[idx] p := b.Preds[idx].b
s.setState(s.endRegs[p.ID]) s.setState(s.endRegs[p.ID])
if s.f.pass.debug > regDebug { if s.f.pass.debug > regDebug {
...@@ -859,13 +860,14 @@ func (s *regAllocState) regalloc(f *Func) { ...@@ -859,13 +860,14 @@ func (s *regAllocState) regalloc(f *Func) {
// desired registers computed during liveness analysis. // desired registers computed during liveness analysis.
// Note that we do this phase after startRegs is set above, so that // Note that we do this phase after startRegs is set above, so that
// we get the right behavior for a block which branches to itself. // we get the right behavior for a block which branches to itself.
for _, succ := range b.Succs { for _, e := range b.Succs {
succ := e.b
// TODO: prioritize likely successor? // TODO: prioritize likely successor?
for _, x := range s.startRegs[succ.ID] { for _, x := range s.startRegs[succ.ID] {
desired.add(x.vid, x.r) desired.add(x.vid, x.r)
} }
// Process phi ops in succ. // Process phi ops in succ.
pidx := predIdx(succ, b) pidx := e.i
for _, v := range succ.Values { for _, v := range succ.Values {
if v.Op != OpPhi { if v.Op != OpPhi {
break break
...@@ -1194,7 +1196,7 @@ func (s *regAllocState) regalloc(f *Func) { ...@@ -1194,7 +1196,7 @@ func (s *regAllocState) regalloc(f *Func) {
// the merge point and promote them to registers now. // the merge point and promote them to registers now.
if len(b.Succs) == 1 { if len(b.Succs) == 1 {
// For this to be worthwhile, the loop must have no calls in it. // For this to be worthwhile, the loop must have no calls in it.
top := b.Succs[0] top := b.Succs[0].b
loop := s.loopnest.b2l[top.ID] loop := s.loopnest.b2l[top.ID]
if loop == nil || loop.header != top || loop.containsCall { if loop == nil || loop.header != top || loop.containsCall {
goto badloop goto badloop
...@@ -1452,7 +1454,7 @@ sinking: ...@@ -1452,7 +1454,7 @@ sinking:
if len(d.Preds) > 1 { if len(d.Preds) > 1 {
panic("Should be impossible given critical edges removed") panic("Should be impossible given critical edges removed")
} }
p := d.Preds[0] // block in loop exiting to d. p := d.Preds[0].b // block in loop exiting to d.
endregs := s.endRegs[p.ID] endregs := s.endRegs[p.ID]
for _, regrec := range endregs { for _, regrec := range endregs {
...@@ -1570,7 +1572,8 @@ func (s *regAllocState) shuffle(stacklive [][]ID) { ...@@ -1570,7 +1572,8 @@ func (s *regAllocState) shuffle(stacklive [][]ID) {
continue continue
} }
e.b = b e.b = b
for i, p := range b.Preds { for i, edge := range b.Preds {
p := edge.b
e.p = p e.p = p
e.setup(i, s.endRegs[p.ID], s.startRegs[b.ID], stacklive[p.ID]) e.setup(i, s.endRegs[p.ID], s.startRegs[b.ID], stacklive[p.ID])
e.process() e.process()
...@@ -2112,18 +2115,19 @@ func (s *regAllocState) computeLive() { ...@@ -2112,18 +2115,19 @@ func (s *regAllocState) computeLive() {
// For each predecessor of b, expand its list of live-at-end values. // For each predecessor of b, expand its list of live-at-end values.
// invariant: live contains the values live at the start of b (excluding phi inputs) // invariant: live contains the values live at the start of b (excluding phi inputs)
for i, p := range b.Preds { for i, e := range b.Preds {
p := e.b
// Compute additional distance for the edge. // Compute additional distance for the edge.
// Note: delta must be at least 1 to distinguish the control // Note: delta must be at least 1 to distinguish the control
// value use from the first user in a successor block. // value use from the first user in a successor block.
delta := int32(normalDistance) delta := int32(normalDistance)
if len(p.Succs) == 2 { if len(p.Succs) == 2 {
if p.Succs[0] == b && p.Likely == BranchLikely || if p.Succs[0].b == b && p.Likely == BranchLikely ||
p.Succs[1] == b && p.Likely == BranchUnlikely { p.Succs[1].b == b && p.Likely == BranchUnlikely {
delta = likelyDistance delta = likelyDistance
} }
if p.Succs[0] == b && p.Likely == BranchUnlikely || if p.Succs[0].b == b && p.Likely == BranchUnlikely ||
p.Succs[1] == b && p.Likely == BranchLikely { p.Succs[1].b == b && p.Likely == BranchLikely {
delta = unlikelyDistance delta = unlikelyDistance
} }
} }
......
...@@ -317,7 +317,7 @@ func mergePoint(b *Block, a ...*Value) *Block { ...@@ -317,7 +317,7 @@ func mergePoint(b *Block, a ...*Value) *Block {
// Don't know which way to go back. Abort. // Don't know which way to go back. Abort.
return nil return nil
} }
b = b.Preds[0] b = b.Preds[0].b
d-- d--
} }
return nil // too far away return nil // too far away
...@@ -341,7 +341,7 @@ found: ...@@ -341,7 +341,7 @@ found:
if len(b.Preds) > 1 { if len(b.Preds) > 1 {
return nil return nil
} }
b = b.Preds[0] b = b.Preds[0].b
d-- d--
} }
......
...@@ -279,8 +279,8 @@ func rewriteBlockARM(b *Block) bool { ...@@ -279,8 +279,8 @@ func rewriteBlockARM(b *Block) bool {
no := b.Succs[1] no := b.Succs[1]
b.Kind = BlockARMLT b.Kind = BlockARMLT
b.SetControl(cc) b.SetControl(cc)
b.Succs[0] = yes _ = yes
b.Succs[1] = no _ = no
return true return true
} }
} }
......
...@@ -9777,8 +9777,7 @@ func rewriteBlockgeneric(b *Block) bool { ...@@ -9777,8 +9777,7 @@ func rewriteBlockgeneric(b *Block) bool {
next := b.Succs[0] next := b.Succs[0]
b.Kind = BlockPlain b.Kind = BlockPlain
b.SetControl(nil) b.SetControl(nil)
b.Succs[0] = next _ = next
b.Likely = BranchUnknown
return true return true
} }
case BlockIf: case BlockIf:
...@@ -9795,9 +9794,9 @@ func rewriteBlockgeneric(b *Block) bool { ...@@ -9795,9 +9794,9 @@ func rewriteBlockgeneric(b *Block) bool {
no := b.Succs[1] no := b.Succs[1]
b.Kind = BlockIf b.Kind = BlockIf
b.SetControl(cond) b.SetControl(cond)
b.Succs[0] = no b.swapSuccessors()
b.Succs[1] = yes _ = no
b.Likely *= -1 _ = yes
return true return true
} }
// match: (If (ConstBool [c]) yes no) // match: (If (ConstBool [c]) yes no)
...@@ -9816,8 +9815,8 @@ func rewriteBlockgeneric(b *Block) bool { ...@@ -9816,8 +9815,8 @@ func rewriteBlockgeneric(b *Block) bool {
} }
b.Kind = BlockFirst b.Kind = BlockFirst
b.SetControl(nil) b.SetControl(nil)
b.Succs[0] = yes _ = yes
b.Succs[1] = no _ = no
return true return true
} }
// match: (If (ConstBool [c]) yes no) // match: (If (ConstBool [c]) yes no)
...@@ -9836,9 +9835,9 @@ func rewriteBlockgeneric(b *Block) bool { ...@@ -9836,9 +9835,9 @@ func rewriteBlockgeneric(b *Block) bool {
} }
b.Kind = BlockFirst b.Kind = BlockFirst
b.SetControl(nil) b.SetControl(nil)
b.Succs[0] = no b.swapSuccessors()
b.Succs[1] = yes _ = no
b.Likely *= -1 _ = yes
return true return true
} }
} }
......
...@@ -28,14 +28,15 @@ func shortcircuit(f *Func) { ...@@ -28,14 +28,15 @@ func shortcircuit(f *Func) {
continue continue
} }
for i, a := range v.Args { for i, a := range v.Args {
p := b.Preds[i] e := b.Preds[i]
p := e.b
if p.Kind != BlockIf { if p.Kind != BlockIf {
continue continue
} }
if p.Control != a { if p.Control != a {
continue continue
} }
if p.Succs[0] == b { if e.i == 0 {
v.SetArg(i, ct) v.SetArg(i, ct)
} else { } else {
v.SetArg(i, cf) v.SetArg(i, cf)
...@@ -91,39 +92,37 @@ func shortcircuit(f *Func) { ...@@ -91,39 +92,37 @@ func shortcircuit(f *Func) {
} }
// The predecessor we come in from. // The predecessor we come in from.
p := b.Preds[i] e1 := b.Preds[i]
p := e1.b
pi := e1.i
// The successor we always go to when coming in // The successor we always go to when coming in
// from that predecessor. // from that predecessor.
t := b.Succs[1-a.AuxInt] e2 := b.Succs[1-a.AuxInt]
t := e2.b
ti := e2.i
// Change the edge p->b to p->t. // Remove b's incoming edge from p.
for j, x := range p.Succs { b.removePred(i)
if x == b { n := len(b.Preds)
p.Succs[j] = t v.Args[i].Uses--
break v.Args[i] = v.Args[n]
} v.Args[n] = nil
} v.Args = v.Args[:n]
// Redirect p's outgoing edge to t.
p.Succs[pi] = Edge{t, len(t.Preds)}
// Fix up t to have one more predecessor. // Fix up t to have one more predecessor.
j := predIdx(t, b) t.Preds = append(t.Preds, Edge{p, pi})
t.Preds = append(t.Preds, p)
for _, w := range t.Values { for _, w := range t.Values {
if w.Op != OpPhi { if w.Op != OpPhi {
continue continue
} }
w.AddArg(w.Args[j]) w.AddArg(w.Args[ti])
} }
// Fix up b to have one less predecessor. if len(b.Preds) == 1 {
n := len(b.Preds) - 1
b.Preds[i] = b.Preds[n]
b.Preds[n] = nil
b.Preds = b.Preds[:n]
v.Args[i].Uses--
v.Args[i] = v.Args[n]
v.Args[n] = nil
v.Args = v.Args[:n]
if n == 1 {
v.Op = OpCopy v.Op = OpCopy
// No longer a phi, stop optimizing here. // No longer a phi, stop optimizing here.
break break
...@@ -132,14 +131,3 @@ func shortcircuit(f *Func) { ...@@ -132,14 +131,3 @@ func shortcircuit(f *Func) {
} }
} }
} }
// predIdx returns the index where p appears in the predecessor list of b.
// p must be in the predecessor list of b.
func predIdx(b, p *Block) int {
for i, x := range b.Preds {
if x == p {
return i
}
}
panic("predecessor not found")
}
...@@ -23,7 +23,7 @@ func TestSizeof(t *testing.T) { ...@@ -23,7 +23,7 @@ func TestSizeof(t *testing.T) {
_64bit uintptr // size on 64bit platforms _64bit uintptr // size on 64bit platforms
}{ }{
{Value{}, 68, 112}, {Value{}, 68, 112},
{Block{}, 124, 232}, {Block{}, 148, 288},
} }
for _, tt := range tests { for _, tt := range tests {
......
...@@ -304,7 +304,8 @@ func (s *stackAllocState) computeLive(spillLive [][]ID) { ...@@ -304,7 +304,8 @@ func (s *stackAllocState) computeLive(spillLive [][]ID) {
// for each predecessor of b, expand its list of live-at-end values // for each predecessor of b, expand its list of live-at-end values
// invariant: s contains the values live at the start of b (excluding phi inputs) // invariant: s contains the values live at the start of b (excluding phi inputs)
for i, p := range b.Preds { for i, e := range b.Preds {
p := e.b
t.clear() t.clear()
t.addAll(s.live[p.ID]) t.addAll(s.live[p.ID])
t.addAll(live.contents()) t.addAll(live.contents())
......
...@@ -7,31 +7,26 @@ package ssa ...@@ -7,31 +7,26 @@ package ssa
// trim removes blocks with no code in them. // trim removes blocks with no code in them.
// These blocks were inserted to remove critical edges. // These blocks were inserted to remove critical edges.
func trim(f *Func) { func trim(f *Func) {
i := 0 n := 0
for _, b := range f.Blocks { for _, b := range f.Blocks {
if b.Kind != BlockPlain || len(b.Values) != 0 || len(b.Preds) != 1 { if b.Kind != BlockPlain || len(b.Values) != 0 || len(b.Preds) != 1 {
f.Blocks[i] = b f.Blocks[n] = b
i++ n++
continue continue
} }
// TODO: handle len(b.Preds)>1 case. // TODO: handle len(b.Preds)>1 case.
// Splice b out of the graph. // Splice b out of the graph.
pred := b.Preds[0] p := b.Preds[0].b
succ := b.Succs[0] i := b.Preds[0].i
for j, s := range pred.Succs { s := b.Succs[0].b
if s == b { j := b.Succs[0].i
pred.Succs[j] = succ p.Succs[i] = Edge{s, j}
s.Preds[j] = Edge{p, i}
} }
tail := f.Blocks[n:]
for i := range tail {
tail[i] = nil
} }
for j, p := range succ.Preds { f.Blocks = f.Blocks[:n]
if p == b {
succ.Preds[j] = pred
}
}
}
for j := i; j < len(f.Blocks); j++ {
f.Blocks[j] = nil
}
f.Blocks = f.Blocks[:i]
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment