Skip to content

Conversation

@a7ehuo
Copy link
Contributor

@a7ehuo a7ehuo commented Mar 6, 2025

There is a case where before block splitter, the merge block is the original fall-through block of the split block and it is also the split block's branch destination. In this case, there is only one edge from the split block to the merge block.

--- Before block splitter ---
BBStart <block_1>  (splitPred)
   ificmpeq ------> block_2

BBStart <block_2>  (mergeNode)

As part of the block splitter transformation, it removes the edge from the split block to its original branch destination. It reverses the split block's branch destination to its original fall-through block. In this case the new branch destination is the same as the original branch destination, but the edge between them is now missing.

We need to create the edge if it doesn't exist.

--- After block splitter  ---
BBStart <block_1>  (splitPred)
   ificmpne -----> block_2     // branch reversed

BBStart <block_3> (cloneStart)

BBStart <block_4> (cloneEnd)

BBStart <block_2> (mergeNode)
-----------------------------

Fixes: eclipse-openj9/openj9#21030

@a7ehuo
Copy link
Contributor Author

a7ehuo commented Mar 6, 2025

@vijaysun-omr May I ask you to review this change? Thank you!

A more detailed description on this block splitter problem can be found in eclipse-openj9/openj9#21030 (comment).

@hzongaro fyi

@vijaysun-omr vijaysun-omr self-assigned this Mar 6, 2025
@vijaysun-omr
Copy link
Contributor

vijaysun-omr commented Mar 6, 2025

Just so I understand this better, if splitPred (block 1) in your example has nothing in between it and block 2 before block splitter was run, is it correct to have blocks 3 and 4 appear in between blocks 1 and 2 if we take the fall through path out of block 1 after block splitter has run ?

@a7ehuo
Copy link
Contributor Author

a7ehuo commented Mar 6, 2025

Here is what happens inside TR_BlockSplitter::splitBlock including how the cloned blocks, block_3 and block_4, are inserted.
block_1: pred or splitPred
block_2: candidate
block_3: cloneStart
block_4: cloneEnd

(1)
After block_3 and block_4 are cloned, the edge block_1-->block_3 (pred-->cloneStart) is added in CFG, and the branch destination edge block_1-->block_2 (pred-->candidate) is removed in CFG, but the block_1's exit tree top is still linked to block_2's entry tree top.

cfg->addEdge(pred, cloneStart);
cfg->removeEdge(pred, candidate);

BBStart <block_1>  (pred or splitPred)
   ificmpeq ------> block_2
      ...
BBEnd </block_1> =====

BBStart <block_2>  (mergeNode)
BBEnd </block_2> =====

(2)
block_4 (cloneEnd) is inserted before block_2. At 6630, pred->getExit()->getNextTreeTop() is the entry of block_2. At 6631, block_3 (cloneStart) is inserted after block_1.

cloneEnd->getExit()->join(pred->getExit()->getNextTreeTop());
pred->getExit()->join(cloneStart->getEntry());

BBStart <block_1>  (pred or splitPred)
   ificmpeq ------> block_2
      ...
BBEnd </block_1> =====

BBStart <block_3>  (cloneStart)
BBEnd </block_3> =====

BBStart <block_4>  (cloneEnd)
BBEnd </block_4> =====

BBStart <block_2>  (mergeNode)
BBEnd </block_2> =====

(3)
Check and reverse the branch inside block_1. cloneEnd is block_4 and cloneEnd->getExit()->getNextTreeTop() is now the entry of block_2 which is the original fall-through block for block_1. At this point in CFG, there is no longer an edge from block_1 to block_2. Because in (1), it removes the branch destination edge block_1-->block_2, which is the same edge for the fall-through path between block_1 and block_2. This is the special case this PR is handling.

if (lastRealNode->getBranchDestination()->getNode()->getBlock()->getNumber() == candidate->getNumber())
{
lastRealNode->reverseBranch(cloneEnd->getExit()->getNextTreeTop());

BBStart <block_1>  (pred or splitPred)
   ificmpne ------> block_2 // branch reversed. The new branch destination is the same as the old branch destination because block_2 was originally block_1's fall-through block and its old branch destination. 
      ...
BBEnd </block_1> =====

BBStart <block_3>  (cloneStart)
BBEnd </block_3> =====

BBStart <block_4>  (cloneEnd)
BBEnd </block_4> =====

BBStart <block_2>  (mergeNode)
BBEnd </block_2> =====

@vijaysun-omr
Copy link
Contributor

Thanks for the detailed explanation, it will come in handy when I review the code later today.

My question was less about the mechanics of how the trees were changed step by step, and more about the logical control flow in the program, i.e. if you look at your prior comment, it was unclear what the original position was for block 3 and block 4 with respect to block 1 and block 2 that were shown in the "before block splitter" snippet.

While your last comment clarified that block 3 and block 4 were located after block 2 in the trees, is the transformation you are showing only applicable if block 2 did not have any code apart from BBStart/BBEnd ? The reason that I ask is otherwise it does not look like the "after block splitter" picture shown


BBStart <block_1>  (pred or splitPred)
   ificmpne ------> block_2 // branch reversed. The new branch destination is the same as the old branch destination because block_2 was originally block_1's fall-through block and its old branch destination. 
      ...
BBEnd </block_1> =====

BBStart <block_3>  (cloneStart)
BBEnd </block_3> =====

BBStart <block_4>  (cloneEnd)
BBEnd </block_4> =====

BBStart <block_2>  (mergeNode)
BBEnd </block_2> =====

is functionally equivalent to the "before block splitter" picture shown

block_1: pred or splitPred
block_2: candidate
block_3: cloneStart
block_4: cloneEnd

@a7ehuo
Copy link
Contributor Author

a7ehuo commented Mar 6, 2025

is the transformation you are showing only applicable if block 2 did not have any code apart from BBStart/BBEnd ? The reason that I ask is otherwise it does not look like the “after block splitter” picture shown

None of the blocks are empty. I showed only BBStart and BBEnd to simplify things.

it was unclear what the original position was for block 3 and block 4 with respect to block 1 and block 2 that were shown in the “before block splitter” snippet.

In this particular case, block_3 is a clone of block_2, and block_4 is a clone of block_2's fall-through block.

Before TR_BlockSplitter::splitBlock
-------------------------------------
block_1: pred or splitPred. The last real tree top is ificmpeq and its branch destination is block_2
block_2: candidate

   cfg 1->2
After TR_BlockSplitter::splitBlock
-------------------------------------

block_1: pred or splitPred. The last real tree top is reversed from ificmpeq into ificmpne. The new branch destination for ificmpne is still block_2 because block_2 was the original fall-through block for block_1
block_3: cloneStart     // clone of block_2
block_4: cloneEnd       // clone of block_2's fall-through block
block_2: candidate

   cfg: 1->3, 3->4      // missing 1->2 

@vijaysun-omr
Copy link
Contributor

Jenkins build all

@vijaysun-omr
Copy link
Contributor

The only suggestion is to add a comment. So I am starting testing anyway.

There is a case where before block splitter, the merge block
is the original fall-through block of the split block and it
is also the split block's branch destination. In this case,
there is only one edge from the split block to the merge block.

--- Before block splitter ---
BBStart <block_1>  (splitPred)
   ificmpeq ------> block_2

BBStart <block_2>  (mergeNode)

As part of the block splitter transformation, it removes the edge
from the split block to its original branch destination. It reverses
the split block's branch destination to its original fall-through
block. In this case the new branch destination is the same as the
original branch destination, but the edge between them is now missing.

We need to create the edge if it doesn't exist.

--- After block splitter  ---
BBStart <block_1>  (splitPred)
   ificmpne -----> block_2     // branch reversed

BBStart <block_3> (cloneStart)

BBStart <block_4> (cloneEnd)

BBStart <block_2> (mergeNode)
-----------------------------

Fixes: eclipse-openj9/openj9#21030
Signed-off-by: Annabelle Huo <Annabelle.Huo@ibm.com>
@a7ehuo a7ehuo force-pushed the fix-block-splitter-missing-edge branch from f30e683 to 6cb52ac Compare March 7, 2025 01:04
@a7ehuo
Copy link
Contributor Author

a7ehuo commented Mar 7, 2025

Jenkins build all

@vijaysun-omr
Copy link
Contributor

Jenkins build amac,riscv

@a7ehuo
Copy link
Contributor Author

a7ehuo commented Mar 7, 2025

Both failures fail in PR #7679 test too.

linux_riscv64 failed with the following error, which also failed in PR #7679 test too. Both PR tests run on: riscv-build1

09:09:21  [WS-CLEANUP] Deleting project workspace...
09:09:21  [WS-CLEANUP] Deferred wipeout is used...
09:09:21  ERROR: Cannot delete workspace :Remote call on riscv-build1 failed

osx_aarch64 failed with porttest, which also failed in PR #7679 test too. Both PR tests run on mac15-aarch64-1

[2025-03-07T14:17:51.280Z] 15: [  FAILED  ] PortSysinfoTest.sysinfo_test_sysinfo_set_limit_ADDRESS_SPACE (0 ms)
...
09:21:46  The following tests FAILED:
09:21:46  	 15 - porttest (Failed)

@vijaysun-omr vijaysun-omr merged commit 23100e7 into eclipse-omr:master Mar 7, 2025
11 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

crash vmState=0x00050fff

2 participants