@@ -12,18 +12,15 @@ type loop struct {
12
12
header * Block // The header node of this (reducible) loop
13
13
outer * loop // loop containing this loop
14
14
15
- // By default, children, exits, and depth are not initialized.
16
- children []* loop // loops nested directly within this loop. Initialized by assembleChildren().
17
- exits []* Block // exits records blocks reached by exits from this loop. Initialized by findExits().
18
-
19
15
// Next three fields used by regalloc and/or
20
16
// aid in computation of inner-ness and list of blocks.
21
17
nBlocks int32 // Number of blocks in this loop but not within inner loops
22
18
depth int16 // Nesting depth of the loop; 1 is outermost. Initialized by calculateDepths().
23
19
isInner bool // True if never discovered to contain a loop
24
20
25
- // register allocation uses this.
26
- containsUnavoidableCall bool // True if all paths through the loop have a call
21
+ // True if all paths through the loop have a call.
22
+ // Computed and used by regalloc; stored here for convenience.
23
+ containsUnavoidableCall bool
27
24
}
28
25
29
26
// outerinner records that outer contains inner
@@ -49,28 +46,13 @@ func (sdom SparseTree) outerinner(outer, inner *loop) {
49
46
outer .isInner = false
50
47
}
51
48
52
- func checkContainsCall (bb * Block ) bool {
53
- if bb .Kind == BlockDefer {
54
- return true
55
- }
56
- for _ , v := range bb .Values {
57
- if opcodeTable [v .Op ].call {
58
- return true
59
- }
60
- }
61
- return false
62
- }
63
-
64
49
type loopnest struct {
65
50
f * Func
66
51
b2l []* loop
67
52
po []* Block
68
53
sdom SparseTree
69
54
loops []* loop
70
55
hasIrreducible bool // TODO current treatment of irreducible loops is very flaky, if accurate loops are needed, must punt at function level.
71
-
72
- // Record which of the lazily initialized fields have actually been initialized.
73
- initializedChildren , initializedDepth , initializedExits bool
74
56
}
75
57
76
58
const (
@@ -355,91 +337,59 @@ func loopnestfor(f *Func) *loopnest {
355
337
visited [b .ID ] = true
356
338
}
357
339
358
- ln := & loopnest {f : f , b2l : b2l , po : po , sdom : sdom , loops : loops , hasIrreducible : sawIrred }
359
-
360
- // Calculate containsUnavoidableCall for regalloc
361
- dominatedByCall := f .Cache .allocBoolSlice (f .NumBlocks ())
362
- defer f .Cache .freeBoolSlice (dominatedByCall )
363
- for _ , b := range po {
364
- if checkContainsCall (b ) {
365
- dominatedByCall [b .ID ] = true
366
- }
367
- }
368
- // Run dfs to find path through the loop that avoids all calls.
369
- // Such path either escapes loop or return back to header.
370
- // It isn't enough to have exit not dominated by any call, for example:
371
- // ... some loop
372
- // call1 call2
373
- // \ /
374
- // exit
375
- // ...
376
- // exit is not dominated by any call, but we don't have call-free path to it.
340
+ // Compute depths.
377
341
for _ , l := range loops {
378
- // Header contains call.
379
- if dominatedByCall [ l . header . ID ] {
380
- l . containsUnavoidableCall = true
342
+ if l . depth != 0 {
343
+ // Already computed because it is an ancestor of
344
+ // a previous loop.
381
345
continue
382
346
}
383
- callfreepath := false
384
- tovisit := make ([]* Block , 0 , len (l .header .Succs ))
385
- // Push all non-loop non-exit successors of header onto toVisit.
386
- for _ , s := range l .header .Succs {
387
- nb := s .Block ()
388
- // This corresponds to loop with zero iterations.
389
- if ! l .iterationEnd (nb , b2l ) {
390
- tovisit = append (tovisit , nb )
347
+ // Find depth by walking up the loop tree.
348
+ d := int16 (0 )
349
+ for x := l ; x != nil ; x = x .outer {
350
+ if x .depth != 0 {
351
+ d += x .depth
352
+ break
391
353
}
354
+ d ++
392
355
}
393
- for len (tovisit ) > 0 {
394
- cur := tovisit [len (tovisit )- 1 ]
395
- tovisit = tovisit [:len (tovisit )- 1 ]
396
- if dominatedByCall [cur .ID ] {
397
- continue
398
- }
399
- // Record visited in dominatedByCall.
400
- dominatedByCall [cur .ID ] = true
401
- for _ , s := range cur .Succs {
402
- nb := s .Block ()
403
- if l .iterationEnd (nb , b2l ) {
404
- callfreepath = true
405
- }
406
- if ! dominatedByCall [nb .ID ] {
407
- tovisit = append (tovisit , nb )
408
- }
409
-
410
- }
411
- if callfreepath {
356
+ // Set depth for every ancestor.
357
+ for x := l ; x != nil ; x = x .outer {
358
+ if x .depth != 0 {
412
359
break
413
360
}
361
+ x .depth = d
362
+ d --
363
+ }
364
+ }
365
+ // Double-check depths.
366
+ for _ , l := range loops {
367
+ want := int16 (1 )
368
+ if l .outer != nil {
369
+ want = l .outer .depth + 1
414
370
}
415
- if ! callfreepath {
416
- l .containsUnavoidableCall = true
371
+ if l . depth != want {
372
+ l .header . Fatalf ( "bad depth calculation for loop %s: got %d want %d" , l . header , l . depth , want )
417
373
}
418
374
}
419
375
376
+ ln := & loopnest {f : f , b2l : b2l , po : po , sdom : sdom , loops : loops , hasIrreducible : sawIrred }
377
+
420
378
// Curious about the loopiness? "-d=ssa/likelyadjust/stats"
421
379
if f .pass != nil && f .pass .stats > 0 && len (loops ) > 0 {
422
- ln .assembleChildren ()
423
- ln .calculateDepths ()
424
- ln .findExits ()
425
380
426
381
// Note stats for non-innermost loops are slightly flawed because
427
382
// they don't account for inner loop exits that span multiple levels.
428
383
429
384
for _ , l := range loops {
430
- x := len (l .exits )
431
- cf := 0
432
- if ! l .containsUnavoidableCall {
433
- cf = 1
434
- }
435
385
inner := 0
436
386
if l .isInner {
437
387
inner ++
438
388
}
439
389
440
- f .LogStat ("loopstats:" ,
441
- l .depth , "depth" , x , "exits" ,
442
- inner , "is_inner" , cf , "always_calls" , l .nBlocks , "n_blocks" )
390
+ f .LogStat ("loopstats in " + f . Name + " :" ,
391
+ l .depth , "depth" ,
392
+ inner , "is_inner" , l .nBlocks , "n_blocks" )
443
393
}
444
394
}
445
395
@@ -465,102 +415,10 @@ func loopnestfor(f *Func) *loopnest {
465
415
return ln
466
416
}
467
417
468
- // assembleChildren initializes the children field of each
469
- // loop in the nest. Loop A is a child of loop B if A is
470
- // directly nested within B (based on the reducible-loops
471
- // detection above)
472
- func (ln * loopnest ) assembleChildren () {
473
- if ln .initializedChildren {
474
- return
475
- }
476
- for _ , l := range ln .loops {
477
- if l .outer != nil {
478
- l .outer .children = append (l .outer .children , l )
479
- }
480
- }
481
- ln .initializedChildren = true
482
- }
483
-
484
- // calculateDepths uses the children field of loops
485
- // to determine the nesting depth (outer=1) of each
486
- // loop. This is helpful for finding exit edges.
487
- func (ln * loopnest ) calculateDepths () {
488
- if ln .initializedDepth {
489
- return
490
- }
491
- ln .assembleChildren ()
492
- for _ , l := range ln .loops {
493
- if l .outer == nil {
494
- l .setDepth (1 )
495
- }
496
- }
497
- ln .initializedDepth = true
498
- }
499
-
500
- // findExits uses loop depth information to find the
501
- // exits from a loop.
502
- func (ln * loopnest ) findExits () {
503
- if ln .initializedExits {
504
- return
505
- }
506
- ln .calculateDepths ()
507
- b2l := ln .b2l
508
- for _ , b := range ln .po {
509
- l := b2l [b .ID ]
510
- if l != nil && len (b .Succs ) == 2 {
511
- sl := b2l [b .Succs [0 ].b .ID ]
512
- if recordIfExit (l , sl , b .Succs [0 ].b ) {
513
- continue
514
- }
515
- sl = b2l [b .Succs [1 ].b .ID ]
516
- if recordIfExit (l , sl , b .Succs [1 ].b ) {
517
- continue
518
- }
519
- }
520
- }
521
- ln .initializedExits = true
522
- }
523
-
524
418
// depth returns the loop nesting level of block b.
525
419
func (ln * loopnest ) depth (b ID ) int16 {
526
420
if l := ln .b2l [b ]; l != nil {
527
421
return l .depth
528
422
}
529
423
return 0
530
424
}
531
-
532
- // recordIfExit checks sl (the loop containing b) to see if it
533
- // is outside of loop l, and if so, records b as an exit block
534
- // from l and returns true.
535
- func recordIfExit (l , sl * loop , b * Block ) bool {
536
- if sl != l {
537
- if sl == nil || sl .depth <= l .depth {
538
- l .exits = append (l .exits , b )
539
- return true
540
- }
541
- // sl is not nil, and is deeper than l
542
- // it's possible for this to be a goto into an irreducible loop made from gotos.
543
- for sl .depth > l .depth {
544
- sl = sl .outer
545
- }
546
- if sl != l {
547
- l .exits = append (l .exits , b )
548
- return true
549
- }
550
- }
551
- return false
552
- }
553
-
554
- func (l * loop ) setDepth (d int16 ) {
555
- l .depth = d
556
- for _ , c := range l .children {
557
- c .setDepth (d + 1 )
558
- }
559
- }
560
-
561
- // iterationEnd checks if block b ends iteration of loop l.
562
- // Ending iteration means either escaping to outer loop/code or
563
- // going back to header
564
- func (l * loop ) iterationEnd (b * Block , b2l []* loop ) bool {
565
- return b == l .header || b2l [b .ID ] == nil || (b2l [b .ID ] != l && b2l [b .ID ].depth <= l .depth )
566
- }
0 commit comments