Skip to content

Commit

Permalink
Extract fgMergeBlockReturn.
Browse files Browse the repository at this point in the history
Morph is already a very vague verb in the Jit, try to use a more precise one.
  • Loading branch information
Sergey Andreenko committed Mar 27, 2020
1 parent eb54d9f commit 04eccab
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 103 deletions.
2 changes: 2 additions & 0 deletions src/coreclr/src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -4429,6 +4429,8 @@ class Compiler
void fgMorphStmts(BasicBlock* block, bool* lnot, bool* loadw);
void fgMorphBlocks();

void fgMergeBlockReturn(BasicBlock* block);

bool fgMorphBlockStmt(BasicBlock* block, Statement* stmt DEBUGARG(const char* msg));

void fgSetOptions();
Expand Down
208 changes: 105 additions & 103 deletions src/coreclr/src/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15755,131 +15755,133 @@ void Compiler::fgMorphBlocks()
}
#endif

/* Process all statement trees in the basic block */

// Process all statement trees in the basic block.
fgMorphStmts(block, &lnot, &loadw);

/* Are we using a single return block? */

// Are we using a single return block?
if (block->bbJumpKind == BBJ_RETURN)
{
if ((genReturnBB != nullptr) && (genReturnBB != block) && ((block->bbFlags & BBF_HAS_JMP) == 0))
{
fgMergeBlockReturn(block);
}
}
block = block->bbNext;
} while (block);

// Note 1: A block is not guaranteed to have a last stmt if its jump kind is BBJ_RETURN.
// For example a method returning void could have an empty block with jump kind BBJ_RETURN.
// Such blocks do materialize as part of in-lining.
//
// Note 2: A block with jump kind BBJ_RETURN does not necessarily need to end with GT_RETURN.
// It could end with a tail call or rejected tail call or monitor.exit or a GT_INTRINSIC.
// For now it is safe to explicitly check whether last stmt is GT_RETURN if genReturnLocal
// is BAD_VAR_NUM.
//
// TODO: Need to characterize the last top level stmt of a block ending with BBJ_RETURN.
/* We are done with the global morphing phase */

Statement* lastStmt = block->lastStmt();
GenTree* ret = (lastStmt != nullptr) ? lastStmt->GetRootNode() : nullptr;
fgGlobalMorph = false;

if ((ret != nullptr) && (ret->OperGet() == GT_RETURN) && ((ret->gtFlags & GTF_RET_MERGED) != 0))
{
// This return was generated during epilog merging, so leave it alone
}
else
{
/* We'll jump to the genReturnBB */
CLANG_FORMAT_COMMENT_ANCHOR;
#ifdef DEBUG
if (verboseTrees)
{
fgDispBasicBlocks(true);
}
#endif
}

void Compiler::fgMergeBlockReturn(BasicBlock* block)
{

// Note 1: A block is not guaranteed to have a last stmt if its jump kind is BBJ_RETURN.
// For example a method returning void could have an empty block with jump kind BBJ_RETURN.
// Such blocks do materialize as part of in-lining.
//
// Note 2: A block with jump kind BBJ_RETURN does not necessarily need to end with GT_RETURN.
// It could end with a tail call or rejected tail call or monitor.exit or a GT_INTRINSIC.
// For now it is safe to explicitly check whether last stmt is GT_RETURN if genReturnLocal
// is BAD_VAR_NUM.
//
// TODO: Need to characterize the last top level stmt of a block ending with BBJ_RETURN.

Statement* lastStmt = block->lastStmt();
GenTree* ret = (lastStmt != nullptr) ? lastStmt->GetRootNode() : nullptr;

if ((ret != nullptr) && (ret->OperGet() == GT_RETURN) && ((ret->gtFlags & GTF_RET_MERGED) != 0))
{
// This return was generated during epilog merging, so leave it alone
}
else
{
/* We'll jump to the genReturnBB */
CLANG_FORMAT_COMMENT_ANCHOR;

#if !defined(TARGET_X86)
if (info.compFlags & CORINFO_FLG_SYNCH)
{
fgConvertSyncReturnToLeave(block);
}
else
if (info.compFlags & CORINFO_FLG_SYNCH)
{
fgConvertSyncReturnToLeave(block);
}
else
#endif // !TARGET_X86
{
block->bbJumpKind = BBJ_ALWAYS;
block->bbJumpDest = genReturnBB;
fgAddRefPred(genReturnBB, block);
fgReturnCount--;
}
if (genReturnLocal != BAD_VAR_NUM)
{
// replace the GT_RETURN node to be a GT_ASG that stores the return value into genReturnLocal.

// Method must be returning a value other than TYP_VOID.
noway_assert(compMethodHasRetVal());

// This block must be ending with a GT_RETURN
noway_assert(lastStmt != nullptr);
noway_assert(lastStmt->GetNextStmt() == nullptr);
noway_assert(ret != nullptr);

// GT_RETURN must have non-null operand as the method is returning the value assigned to
// genReturnLocal
noway_assert(ret->OperGet() == GT_RETURN);
noway_assert(ret->gtGetOp1() != nullptr);

Statement* pAfterStatement = lastStmt;
IL_OFFSETX offset = lastStmt->GetILOffsetX();
GenTree* tree =
gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, offset, block);
if (tree->OperIsCopyBlkOp())
{
tree = fgMorphCopyBlock(tree);
}
{
block->bbJumpKind = BBJ_ALWAYS;
block->bbJumpDest = genReturnBB;
fgAddRefPred(genReturnBB, block);
fgReturnCount--;
}
if (genReturnLocal != BAD_VAR_NUM)
{
// replace the GT_RETURN node to be a GT_ASG that stores the return value into genReturnLocal.

if (pAfterStatement == lastStmt)
{
lastStmt->SetRootNode(tree);
}
else
{
// gtNewTempAssign inserted additional statements after last
fgRemoveStmt(block, lastStmt);
Statement* newStmt = gtNewStmt(tree, offset);
fgInsertStmtAfter(block, pAfterStatement, newStmt);
lastStmt = newStmt;
}
// Method must be returning a value other than TYP_VOID.
noway_assert(compMethodHasRetVal());

// make sure that copy-prop ignores this assignment.
lastStmt->GetRootNode()->gtFlags |= GTF_DONT_CSE;
}
else if (ret != nullptr && ret->OperGet() == GT_RETURN)
{
// This block ends with a GT_RETURN
noway_assert(lastStmt != nullptr);
noway_assert(lastStmt->GetNextStmt() == nullptr);
// This block must be ending with a GT_RETURN
noway_assert(lastStmt != nullptr);
noway_assert(lastStmt->GetNextStmt() == nullptr);
noway_assert(ret != nullptr);

// Must be a void GT_RETURN with null operand; delete it as this block branches to oneReturn
// block
noway_assert(ret->TypeGet() == TYP_VOID);
noway_assert(ret->gtGetOp1() == nullptr);
// GT_RETURN must have non-null operand as the method is returning the value assigned to
// genReturnLocal
noway_assert(ret->OperGet() == GT_RETURN);
noway_assert(ret->gtGetOp1() != nullptr);

fgRemoveStmt(block, lastStmt);
}
#ifdef DEBUG
if (verbose)
{
printf("morph " FMT_BB " to point at onereturn. New block is\n", block->bbNum);
fgTableDispBasicBlock(block);
}
#endif
}
Statement* pAfterStatement = lastStmt;
IL_OFFSETX offset = lastStmt->GetILOffsetX();
GenTree* tree = gtNewTempAssign(genReturnLocal, ret->gtGetOp1(), &pAfterStatement, offset, block);
if (tree->OperIsCopyBlkOp())
{
tree = fgMorphCopyBlock(tree);
}
}
block = block->bbNext;
} while (block);

/* We are done with the global morphing phase */
if (pAfterStatement == lastStmt)
{
lastStmt->SetRootNode(tree);
}
else
{
// gtNewTempAssign inserted additional statements after last
fgRemoveStmt(block, lastStmt);
Statement* newStmt = gtNewStmt(tree, offset);
fgInsertStmtAfter(block, pAfterStatement, newStmt);
lastStmt = newStmt;
}

fgGlobalMorph = false;
// make sure that copy-prop ignores this assignment.
lastStmt->GetRootNode()->gtFlags |= GTF_DONT_CSE;
}
else if (ret != nullptr && ret->OperGet() == GT_RETURN)
{
// This block ends with a GT_RETURN
noway_assert(lastStmt != nullptr);
noway_assert(lastStmt->GetNextStmt() == nullptr);

// Must be a void GT_RETURN with null operand; delete it as this block branches to oneReturn
// block
noway_assert(ret->TypeGet() == TYP_VOID);
noway_assert(ret->gtGetOp1() == nullptr);

fgRemoveStmt(block, lastStmt);
}
#ifdef DEBUG
if (verboseTrees)
{
fgDispBasicBlocks(true);
}
if (verbose)
{
printf("morph " FMT_BB " to point at onereturn. New block is\n", block->bbNum);
fgTableDispBasicBlock(block);
}
#endif
}
}

/*****************************************************************************
Expand Down

0 comments on commit 04eccab

Please sign in to comment.