Skip to content

JIT: If-conversion phase can produce questionable IR. Return block having a successor #124942

@BoyBaykiller

Description

@BoyBaykiller

I am doing some work on if-conversion and noticed that it can produce IR where a block with a return statement has a successor. Consider this minimal reproducible:

static int InvalidIR(bool cond)
{
    if (cond)
    {
        return 2;
    }
    // or else {}
    return 4;
}

Input to if-conversion:

------------ BB01 [0000] [000..003) -> BB03(0.5),BB02(0.5) (cond), preds={} succs={BB02,BB03}

***** BB01 [0000]
STMT00000 ( 0x000[E--] ... 0x001 )
N005 (  7,  8) [000003] -----+-----                         *  JTRUE     void   $VN.Void
N004 (  5,  6) [000002] J----+-N---                         \--*  EQ        int    $101
N002 (  3,  4) [000008] -----+-----                            +--*  CAST      int <- ubyte <- int $100
N001 (  2,  2) [000000] -----+-----                            |  \--*  LCL_VAR   int    V00 arg0         u:1 (last use) $80
N003 (  1,  1) [000001] -----+-----                            \--*  CNS_INT   int    0 $40

------------ BB02 [0001] [003..005) (return), preds={BB01} succs={}

***** BB02 [0001]
STMT00002 ( 0x003[E--] ... 0x004 )
N002 (  2,  2) [000007] -----+-----                         *  RETURN    int    $VN.Void
N001 (  1,  1) [000006] -----+-----                         \--*  CNS_INT   int    2 $44

------------ BB03 [0002] [005..007) (return), preds={BB01} succs={}

***** BB03 [0002]
STMT00001 ( 0x005[E--] ... 0x006 )
N002 (  2,  2) [000005] -----+-----                         *  RETURN    int    $VN.Void
N001 (  1,  1) [000004] -----+-----                         \--*  CNS_INT   int    4 $43

Then-case is BB02, else-case is BB03. Their statements are set to null and the SELECT node gets created in the JTRUE block (BB01). However the JTRUE block only get's it BB02 successor removed (and is set to BBJ_ALWAYS):

FlowEdge* const removedEdge = m_compiler->fgRemoveAllRefPreds(m_startBlock->GetFalseTarget(), m_startBlock);
FlowEdge* const retainedEdge = m_startBlock->GetTrueEdge();
m_startBlock->SetKindAndTargetEdge(BBJ_ALWAYS, retainedEdge);
m_compiler->fgRepairProfileCondToUncond(m_startBlock, retainedEdge, removedEdge);

Which leads to the following output where BB01 has a return statement and a successor at the same time:

------------ BB01 [0000] [000..003) -> BB03(1) (always), preds={} succs={BB03}

***** BB01 [0000]
STMT00000 ( 0x000[E--] ... 0x001 )
N001 (  0,  0) [000003] -----+-----                         *  NOP       void  

***** BB01 [0000]
STMT00002 ( 0x003[E--] ... 0x004 )
N008 (  9, 10) [000007] -----+-----                         *  RETURN    int    $VN.Void
N007 (  8,  9) [000009] -----------                         \--*  SELECT    int   
N004 (  5,  6) [000002] J----+-N---                            +--*  EQ        int    $101
N002 (  3,  4) [000008] -----+-----                            |  +--*  CAST      int <- ubyte <- int $100
N001 (  2,  2) [000000] -----+-----                            |  |  \--*  LCL_VAR   int    V00 arg0         u:1 (last use) $80
N003 (  1,  1) [000001] -----+-----                            |  \--*  CNS_INT   int    0 $40
N005 (  1,  1) [000004] -----+-----                            +--*  CNS_INT   int    4 $43
N006 (  1,  1) [000006] -----+-----                            \--*  CNS_INT   int    2 $44

***** BB01 [0000]
STMT00001 ( 0x005[E--] ... 0x006 )
N001 (  0,  0) [000005] -----+-----                         *  NOP       void  

------------ BB02 [0001] [003..005) (return), preds={} succs={}

------------ BB03 [0002] [005..007) (return), preds={BB01} succs={}

I think this is also the reason it fails to recognize the second (from bottom to top) if-else flow here: https://godbolt.org/z/Kb5854sM1. So fixing this should make it catch a few more cases. But I am not sure how to do so in practice.

Metadata

Metadata

Assignees

Labels

Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions