@@ -200,19 +200,21 @@ blockloop:
200
200
// last block of src chain. Once all edges are processed, the chains are sorted
201
201
// by hottness and merge count and generate final block order.
202
202
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.
204
206
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
208
210
}
209
211
210
- func (t * chain ) first () * Block {
211
- return t .blocks [0 ]
212
+ func (c * chain ) first () * Block {
213
+ return c .blocks [0 ]
212
214
}
213
215
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 ]
216
218
}
217
219
218
220
// edge simply represents a CFG edge
@@ -231,32 +233,36 @@ func (e *edge) String() string {
231
233
return fmt .Sprintf ("%v->%v(%d)" , e .src , e .dst , e .weight )
232
234
}
233
235
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.
234
239
type chainGraph struct {
235
- chainId int
240
+ cid idAlloc
236
241
chains []* chain
237
242
edges []* edge
238
- b2chain map [ * Block ]* chain
243
+ b2chain [ ]* chain // indexed by block id
239
244
}
240
245
241
246
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
247
251
}
248
252
249
253
func (g * chainGraph ) getChain (b * Block ) * chain {
250
- return g .b2chain [b ]
254
+ return g .b2chain [b . ID ]
251
255
}
252
256
257
+ // mergeChain merges the "from" chain into the "to" chain. The from chain is
258
+ // removed then.
253
259
func (g * chainGraph ) mergeChain (to , from * chain ) {
254
260
for _ , block := range from .blocks {
255
- g .b2chain [block ] = to
261
+ g .b2chain [block . ID ] = to
256
262
}
257
263
to .blocks = append (to .blocks , from .blocks ... )
258
264
to .priority ++ // increment
259
- g .chains [from .id ] = nil
265
+ g .chains [from .id - 1 /*ID always >0*/ ] = nil
260
266
}
261
267
262
268
func (g * chainGraph ) print () {
@@ -274,7 +280,11 @@ func (g *chainGraph) print() {
274
280
}
275
281
276
282
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
+ }
278
288
279
289
// Initially every block is in its own chain
280
290
for _ , block := range fn .Blocks {
@@ -301,17 +311,13 @@ func greedyBlockOrder(fn *Func) []*Block {
301
311
}
302
312
303
313
// 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
- }
313
314
sort .SliceStable (graph .edges , func (i , j int ) bool {
314
315
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
+
315
321
// If the weights are the same, then keep the original order, this
316
322
// ensures that adjacent edges are accessed sequentially, which has
317
323
// a noticeable impact on performance
@@ -339,16 +345,19 @@ func greedyBlockOrder(fn *Func) []*Block {
339
345
graph .mergeChain (src , dst )
340
346
}
341
347
}
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 ++
350
358
}
351
359
}
360
+ graph .chains = graph .chains [:i ]
352
361
353
362
// Reorder chains based by hottness and priority
354
363
before := make (map [* chain ][]* chain )
@@ -363,10 +372,15 @@ func greedyBlockOrder(fn *Func) []*Block {
363
372
before [src ] = append (before [src ], dst )
364
373
}
365
374
}
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
+ }
370
384
// Respect precedence relation
371
385
for _ , b := range before [c1 ] {
372
386
if b == c2 {
@@ -382,8 +396,9 @@ func greedyBlockOrder(fn *Func) []*Block {
382
396
return s1 > s2
383
397
}
384
398
// Keep original order if we can't decide
385
- return true
399
+ return false
386
400
})
401
+ assert (graph .chains [0 ].first () == fn .Entry , "entry chain must be first" )
387
402
388
403
// Generate final block order
389
404
blockOrder := make ([]* Block , 0 )
0 commit comments