Skip to content

Sort cf blocks in program order - #1049

Merged
Jiahui17 merged 2 commits into
EPFL-LAP:mainfrom
QinYuan2000:sortblocks
Oct 12, 2026
Merged

Jiahui17 merged 2 commits into
EPFL-LAP:mainfrom
QinYuan2000:sortblocks

Conversation

@QinYuan2000

@QinYuan2000 QinYuan2000 commented Sep 14, 2026 •

Copy link
Copy Markdown
Collaborator

Several parts of the flow assume that basic block IDs reflect program order. In particular, isBackedge explicitly states:

"Block IDs are ordered in original program order, so an edge going from a higher block ID to a lower block ID is a backedge."

However, both StdProfiler::writeCSV and LowerFuncToHandshake::idBasicBlocks derive block IDs directly from their positions in the region, and nothing currently guarantees this order.

This assumption happened to hold for kmp before, but recent frontend changes altered its CFG/block layout and exposed the underlying ordering issue. In the current ordering, bb4 -> bb2 and bb9 -> bb7 are classified as backedges by their IDs, although neither destination dominates its source. By the standard definition of a backedge (Dragon Book, Sec. 9.6.4), the destination of a backedge must dominate its source.

Current ordering, which exposes the issue:

kmp_CFG_current

Earlier ordering, where the assumption happened to hold:

kmp_CFG_previous

This PR adds --sort-blocks-in-program-order, which explicitly enforces the expected ordering by reordering blocks in reverse postorder (RPO) before their IDs are assigned. RPO gives the ordering expected by the existing ID-based backedge logic for reducible CFGs. Clang similarly uses an RPO-based CFG order and explicitly adjusts it to place loop bodies before loop successors (PostOrderCFGView).

The pass runs before profiling and CF-to-Handshake conversion diverge, keeping the block IDs in the frequency CSV and handshake.bb consistent.

@Jiahui17

Copy link
Copy Markdown
Member
image

How do you prevent DFS from visiting BB4 first?

@QinYuan2000

Copy link
Copy Markdown
Collaborator Author

@Jiahui17 In RPO, a block is added to the postorder only after all its successors have been explored. Regardless of which one is visited first, bb4 always finishes before bb2, since bb4 must be finished before bb2 can be finished. Therefore, bb4 is always added to the postorder before bb2, and after reversing the postorder, bb2 has a smaller index than bb4.

Comment thread include/dynamatic/Transforms/Passes.td Outdated
}];
}

def SortBlocksInProgramOrder : DynamaticPass<"sort-blocks-in-program-order"> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DynamaticPass is deprecated, use Pass instead


"program order" is vague, just use RPO-based CFG order or something similar


/// Returns the blocks of the region in reverse postorder, starting from its
/// entry block. Blocks which the entry cannot reach come last.
static SmallVector<Block *> getBlocksInProgramOrder(Region &region) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using reverse post order instead of program order


// Moving each block right before the next one, from the last to the first,
// leaves the region in that same order
for (size_t idx = order.size(); idx-- > 1;)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just do idx > 1; --idx

Comment on lines +46 to +61
while (!stack.empty()) {
Block *block = stack.back().first;
unsigned nextSuccessor = stack.back().second;

if (nextSuccessor < block->getNumSuccessors()) {
stack.back().second = nextSuccessor + 1;
Block *successor = block->getSuccessor(nextSuccessor);
if (visited.insert(successor).second)
stack.push_back({successor, 0});
continue;
}

// All the successors were explored, thus the block is in postorder position
programOrder.push_back(block);
stack.pop_back();
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you pls describe what's going on here?

@QinYuan2000

Copy link
Copy Markdown
Collaborator Author

@Jiahui17 Hi Jiahui, thanks for your comments! I’ve addressed them in 5e561a5.

unsigned nextSuccessor = stack.back().second;

// If there is another successor to explore, remember to continue with the
// following successor when returning to this block.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • "when returning..." When does it return generally?
  • "...To this block" is the current block or the nextSuccessor?

Also, why do we do this?

stack.back().second = nextSuccessor + 1;
Block *successor = block->getSuccessor(nextSuccessor);

// Visit the successor if it has not been reached through another path.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, it is unclear WHY we do this

@Jiahui17
Jiahui17 merged commit 760156a into EPFL-LAP:main Oct 12, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants