Skip to content

Commit 1e61a84

Browse files
committed
address keith comments
1 parent be2f319 commit 1e61a84

File tree

2 files changed

+62
-41
lines changed

2 files changed

+62
-41
lines changed

src/cmd/compile/internal/ssa/func.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ type LocalSlotSplitKey struct {
8686
Type *types.Type // type of slot
8787
}
8888

89+
func assert(cond bool, fx string, msg ...interface{}) {
90+
if !cond {
91+
panic(fmt.Sprintf(fx, msg...))
92+
}
93+
}
94+
8995
// NewFunc returns a new, empty function object.
9096
// Caller must reset cache before calling NewFunc.
9197
func (c *Config) NewFunc(fe Frontend, cache *Cache) *Func {

src/cmd/compile/internal/ssa/layout.go

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -200,19 +200,21 @@ blockloop:
200200
// last block of src chain. Once all edges are processed, the chains are sorted
201201
// by hottness and merge count and generate final block order.
202202

203-
// chain is a linear sequence of blocks
203+
// chain is a linear sequence of blocks, where the first block is the entry block
204+
// and the last block is the exit block. The chain is used to represent a sequence
205+
// of blocks that are likely to be executed together.
204206
type chain struct {
205-
id int
206-
blocks []*Block
207-
priority int // merge count
207+
id ID
208+
blocks []*Block // ordered blocks in this chain
209+
priority int // merge count
208210
}
209211

210-
func (t *chain) first() *Block {
211-
return t.blocks[0]
212+
func (c *chain) first() *Block {
213+
return c.blocks[0]
212214
}
213215

214-
func (t *chain) last() *Block {
215-
return t.blocks[len(t.blocks)-1]
216+
func (c *chain) last() *Block {
217+
return c.blocks[len(c.blocks)-1]
216218
}
217219

218220
// edge simply represents a CFG edge
@@ -231,32 +233,36 @@ func (e *edge) String() string {
231233
return fmt.Sprintf("%v->%v(%d)", e.src, e.dst, e.weight)
232234
}
233235

236+
// chainGraph is a directed graph of chains, where each chain is a sequence of
237+
// blocks, and each edge is a CFG edge with a weight. The graph is used to build
238+
// a block layout that minimizes control flow instructions.
234239
type chainGraph struct {
235-
chainId int
240+
cid idAlloc
236241
chains []*chain
237242
edges []*edge
238-
b2chain map[*Block]*chain
243+
b2chain []*chain // indexed by block id
239244
}
240245

241246
func (g *chainGraph) newChain(block *Block) *chain {
242-
tr := &chain{g.chainId, []*Block{block}, 0 /*priority*/}
243-
g.b2chain[block] = tr
244-
g.chains = append(g.chains, tr)
245-
g.chainId++
246-
return tr
247+
c := &chain{g.cid.get(), []*Block{block}, 0 /*priority*/}
248+
g.b2chain[block.ID] = c
249+
g.chains = append(g.chains, c)
250+
return c
247251
}
248252

249253
func (g *chainGraph) getChain(b *Block) *chain {
250-
return g.b2chain[b]
254+
return g.b2chain[b.ID]
251255
}
252256

257+
// mergeChain merges the "from" chain into the "to" chain. The from chain is
258+
// removed then.
253259
func (g *chainGraph) mergeChain(to, from *chain) {
254260
for _, block := range from.blocks {
255-
g.b2chain[block] = to
261+
g.b2chain[block.ID] = to
256262
}
257263
to.blocks = append(to.blocks, from.blocks...)
258264
to.priority++ // increment
259-
g.chains[from.id] = nil
265+
g.chains[from.id-1 /*ID always >0*/] = nil
260266
}
261267

262268
func (g *chainGraph) print() {
@@ -274,7 +280,11 @@ func (g *chainGraph) print() {
274280
}
275281

276282
func greedyBlockOrder(fn *Func) []*Block {
277-
graph := &chainGraph{0, []*chain{}, []*edge{}, make(map[*Block]*chain)}
283+
graph := &chainGraph{
284+
chains: []*chain{},
285+
edges: []*edge{},
286+
b2chain: make([]*chain, fn.NumBlocks(), fn.NumBlocks()),
287+
}
278288

279289
// Initially every block is in its own chain
280290
for _, block := range fn.Blocks {
@@ -301,17 +311,13 @@ func greedyBlockOrder(fn *Func) []*Block {
301311
}
302312

303313
// Sort edges by weight and move slow path to end
304-
j := len(graph.edges) - 1
305-
for i, edge := range graph.edges {
306-
if edge.weight == 0 {
307-
if edge.dst.Kind == BlockExit && i < j {
308-
graph.edges[j], graph.edges[i] = graph.edges[i], graph.edges[j]
309-
j--
310-
}
311-
}
312-
}
313314
sort.SliceStable(graph.edges, func(i, j int) bool {
314315
e1, e2 := graph.edges[i], graph.edges[j]
316+
// Move slow path to end
317+
if e1.weight == WeightNotTaken && e2.weight == WeightNotTaken {
318+
return e1.dst.Kind != BlockExit && e2.dst.Kind == BlockExit
319+
}
320+
315321
// If the weights are the same, then keep the original order, this
316322
// ensures that adjacent edges are accessed sequentially, which has
317323
// a noticeable impact on performance
@@ -339,16 +345,19 @@ func greedyBlockOrder(fn *Func) []*Block {
339345
graph.mergeChain(src, dst)
340346
}
341347
}
342-
for i := 0; i < len(graph.chains); i++ {
343-
// Remove nil chains because they are merged
344-
if graph.chains[i] == nil {
345-
graph.chains = append(graph.chains[:i], graph.chains[i+1:]...)
346-
i--
347-
} else if graph.chains[i].first() == fn.Entry {
348-
// Entry chain must be present at beginning
349-
graph.chains[0], graph.chains[i] = graph.chains[i], graph.chains[0]
348+
i := 0
349+
for _, chain := range graph.chains {
350+
// Remove nil chains because they are merge
351+
if chain != nil {
352+
graph.chains[i] = chain
353+
if chain.first() == fn.Entry {
354+
// Entry chain must be present at beginning
355+
graph.chains[0], graph.chains[i] = graph.chains[i], graph.chains[0]
356+
}
357+
i++
350358
}
351359
}
360+
graph.chains = graph.chains[:i]
352361

353362
// Reorder chains based by hottness and priority
354363
before := make(map[*chain][]*chain)
@@ -363,10 +372,15 @@ func greedyBlockOrder(fn *Func) []*Block {
363372
before[src] = append(before[src], dst)
364373
}
365374
}
366-
// assert(graph.chains[0].first() == fn.Entry, "entry chain must be first")
367-
const idxSkipEntry = 1 // Entry chain is always first
368-
sort.SliceStable(graph.chains[idxSkipEntry:], func(i, j int) bool {
369-
c1, c2 := graph.chains[i+idxSkipEntry], graph.chains[j+idxSkipEntry]
375+
sort.SliceStable(graph.chains, func(i, j int) bool {
376+
c1, c2 := graph.chains[i], graph.chains[j]
377+
// Entry chain must be present at beginning
378+
if c1.first() == fn.Entry {
379+
return true
380+
}
381+
if c2.first() == fn.Entry {
382+
return false
383+
}
370384
// Respect precedence relation
371385
for _, b := range before[c1] {
372386
if b == c2 {
@@ -382,8 +396,9 @@ func greedyBlockOrder(fn *Func) []*Block {
382396
return s1 > s2
383397
}
384398
// Keep original order if we can't decide
385-
return true
399+
return false
386400
})
401+
assert(graph.chains[0].first() == fn.Entry, "entry chain must be first")
387402

388403
// Generate final block order
389404
blockOrder := make([]*Block, 0)

0 commit comments

Comments
 (0)