@@ -61,15 +61,15 @@ function asTrueLH(block: Block | undefined): LoopHeader {
6161 if ( isTrueLH ( block ) ) {
6262 return block ;
6363 }
64- throw new Error ( " Block is not a LoopHeader" ) ;
64+ throw new Error ( ` Block ${ block . id } is not a LoopHeader` ) ;
6565}
6666
6767function asLH ( block : Block | undefined ) : LoopHeader {
6868 assert ( block ) ;
6969 if ( isLH ( block ) ) {
7070 return block as LoopHeader ;
7171 }
72- throw new Error ( " Block is not a pseudo LoopHeader" ) ;
72+ throw new Error ( ` Block ${ block . id } is not a pseudo LoopHeader` ) ;
7373}
7474
7575type LayoutNode = BlockNode | DummyNode ;
@@ -344,11 +344,12 @@ export class Graph {
344344 }
345345
346346 private layout ( ) : [ LayoutNode [ ] [ ] , number [ ] , number [ ] ] {
347- const roots = this . blocks . filter ( b => b . predecessors . length === 0 ) ;
347+ const [ roots , osrBlocks ] = this . findLayoutRoots ( ) ;
348+ log . log ( "Layout roots:" , roots . map ( r => r . id ) ) ;
348349
349- // Make the roots into pseudo loop headers.
350- for ( const r of roots ) {
351- const root = r as LoopHeader ;
350+ // Make the roots and OSR blocks into pseudo loop headers.
351+ for ( const b of [ ... roots , ... osrBlocks ] ) {
352+ const root = b as LoopHeader ;
352353 root . loopHeight = 0 ;
353354 root . parentLoop = null ;
354355 root . outgoingEdges = [ ] ;
@@ -366,8 +367,15 @@ export class Graph {
366367 log . groupEnd ( ) ;
367368 }
368369 for ( const r of roots ) {
370+ log . group ( "layer" ) ;
369371 this . layer ( r ) ;
372+ log . groupEnd ( ) ;
370373 }
374+ for ( const b of osrBlocks ) {
375+ b . layer = 0 ;
376+ b . loopID = b . id ;
377+ }
378+
371379 const layoutNodesByLayer = this . makeLayoutNodes ( ) ;
372380 this . straightenEdges ( layoutNodesByLayer ) ;
373381 const trackHeights = this . finagleJoints ( layoutNodesByLayer ) ;
@@ -376,6 +384,44 @@ export class Graph {
376384 return [ layoutNodesByLayer , layerHeights , trackHeights ] ;
377385 }
378386
387+ // Finds the "layout roots" of the graph, which are the blocks we should
388+ // start the layout process from. This may not always equal the true roots
389+ // (blocks without predecessors), because OSR blocks can cause us to begin
390+ // layout at arbitrary points in the graph.
391+ private findLayoutRoots ( ) : [ Block [ ] , Block [ ] ] {
392+ const newRoots : Block [ ] = [ ] ;
393+ const osrBlocks : Block [ ] = [ ] ;
394+ const roots = this . blocks . filter ( b => b . predecessors . length === 0 ) ;
395+ for ( const root of roots ) {
396+ let newRoot = root ;
397+ if ( root . attributes . includes ( "osr" ) ) {
398+ assert ( root . succs . length > 0 ) ;
399+ osrBlocks . push ( root ) ;
400+ newRoot = root . succs [ 0 ] ;
401+
402+ // Walk up the graph by repeatedly choosing the first non-OSR, non-
403+ // backedge predecessor.
404+ for ( let j = 0 ; ; j ++ ) {
405+ if ( j >= 10_000_000 ) {
406+ throw new Error ( "likely infinite loop" ) ;
407+ }
408+
409+ const validPredecessors = newRoot . preds . filter ( p => ! p . attributes . includes ( "osr" ) && ! p . attributes . includes ( "backedge" ) ) ;
410+ if ( validPredecessors . length === 0 ) {
411+ break ;
412+ }
413+ newRoot = validPredecessors [ 0 ] ;
414+ }
415+ }
416+
417+ // Track and deduplicate
418+ if ( ! newRoots . includes ( newRoot ) ) {
419+ newRoots . push ( newRoot ) ;
420+ }
421+ }
422+ return [ newRoots , osrBlocks ] ;
423+ }
424+
379425 // Walks through the graph tracking which loop each block belongs to. As
380426 // each block is visited, it is assigned the current loop ID. If the
381427 // block has lesser loopDepth than its parent, that means it is outside
@@ -392,6 +438,7 @@ export class Graph {
392438 }
393439
394440 log . log ( "block:" , block . id , block . loopDepth , "loopIDsByDepth:" , loopIDsByDepth ) ;
441+ log . log ( block . attributes ) ;
395442
396443 if ( isTrueLH ( block ) ) {
397444 const parentID = loopIDsByDepth [ loopIDsByDepth . length - 1 ] ;
@@ -427,6 +474,8 @@ export class Graph {
427474 }
428475
429476 private layer ( block : Block , layer = 0 ) {
477+ log . log ( "block" , block . id , "layer" , layer ) ;
478+
430479 if ( block . attributes . includes ( "backedge" ) ) {
431480 block . layer = block . succs [ 0 ] . layer ;
432481 return ;
@@ -464,6 +513,8 @@ export class Graph {
464513 }
465514
466515 private makeLayoutNodes ( ) : LayoutNode [ ] [ ] {
516+ log . group ( "makeLayoutNodes" ) ;
517+
467518 function connectNodes ( from : LayoutNode , fromPort : number , to : LayoutNode ) {
468519 from . dstNodes [ fromPort ] = to ;
469520 if ( ! to . srcNodes . includes ( from ) ) {
@@ -498,6 +549,8 @@ export class Graph {
498549 const activeEdges : IncompleteEdge [ ] = [ ] ;
499550 const latestDummiesForBackedges = new Map < Block , DummyNode > ( ) ;
500551 for ( const [ layer , blocks ] of blocksByLayer . entries ( ) ) {
552+ log . group ( "layer" , layer , "blocks" , blocks . map ( b => b . id ) ) ;
553+
501554 // Delete any active edges that terminate at this layer, since we do
502555 // not want to make any dummy nodes for them.
503556 const terminatingEdges : IncompleteEdge [ ] = [ ] ;
@@ -639,6 +692,8 @@ export class Graph {
639692 const backedgeDummy = must ( latestDummiesForBackedges . get ( edge . dstBlock ) ) ;
640693 connectNodes ( edge . src , edge . srcPort , backedgeDummy ) ;
641694 }
695+
696+ log . groupEnd ( ) ;
642697 }
643698
644699 // Prune backedge dummies that don't have a source. This can happen because
@@ -704,6 +759,7 @@ export class Graph {
704759 }
705760 }
706761
762+ log . groupEnd ( ) ;
707763 return layoutNodesByLayer ;
708764 }
709765
0 commit comments