-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[CodeExtractor] Terminate callsite blocks to new noreturn
functions with unreachable
#84682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1010,6 +1010,18 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, | |
|
||
newFunction->addFnAttr(Attr); | ||
} | ||
|
||
if (NumExitBlocks == 0) { | ||
// Mark the new function `noreturn` if applicable. Terminators which resume | ||
// exception propagation are treated as returning instructions. This is to | ||
// avoid inserting traps after calls to outlined functions which unwind. | ||
if (none_of(Blocks, [](const BasicBlock *BB) { | ||
const Instruction *Term = BB->getTerminator(); | ||
return isa<ReturnInst>(Term) || isa<ResumeInst>(Term); | ||
})) | ||
newFunction->setDoesNotReturn(); | ||
} | ||
|
||
newFunction->insert(newFunction->end(), newRootNode); | ||
|
||
// Create scalar and aggregate iterators to name all of the arguments we | ||
|
@@ -1392,19 +1404,23 @@ CallInst *CodeExtractor::emitCallAndSwitchStatement(Function *newFunction, | |
case 0: | ||
// There are no successors (the block containing the switch itself), which | ||
// means that previously this was the last part of the function, and hence | ||
// this should be rewritten as a `ret' | ||
|
||
// Check if the function should return a value | ||
if (OldFnRetTy->isVoidTy()) { | ||
ReturnInst::Create(Context, nullptr, TheSwitch->getIterator()); // Return void | ||
// this should be rewritten as a `ret` or `unreachable`. | ||
if (newFunction->doesNotReturn()) { | ||
// If fn is no return, end with an unreachable terminator. | ||
(void)new UnreachableInst(Context, TheSwitch->getIterator()); | ||
} else if (OldFnRetTy->isVoidTy()) { | ||
// We have no return value. | ||
ReturnInst::Create(Context, nullptr, | ||
TheSwitch->getIterator()); // Return void | ||
} else if (OldFnRetTy == TheSwitch->getCondition()->getType()) { | ||
// return what we have | ||
ReturnInst::Create(Context, TheSwitch->getCondition(), TheSwitch->getIterator()); | ||
ReturnInst::Create(Context, TheSwitch->getCondition(), | ||
TheSwitch->getIterator()); | ||
} else { | ||
// Otherwise we must have code extracted an unwind or something, just | ||
// return whatever we want. | ||
ReturnInst::Create(Context, | ||
Constant::getNullValue(OldFnRetTy), TheSwitch->getIterator()); | ||
ReturnInst::Create(Context, Constant::getNullValue(OldFnRetTy), | ||
TheSwitch->getIterator()); | ||
} | ||
|
||
TheSwitch->eraseFromParent(); | ||
|
@@ -1895,16 +1911,6 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC, | |
|
||
fixupDebugInfoPostExtraction(*oldFunction, *newFunction, *TheCall); | ||
|
||
// Mark the new function `noreturn` if applicable. Terminators which resume | ||
// exception propagation are treated as returning instructions. This is to | ||
// avoid inserting traps after calls to outlined functions which unwind. | ||
bool doesNotReturn = none_of(*newFunction, [](const BasicBlock &BB) { | ||
const Instruction *Term = BB.getTerminator(); | ||
return isa<ReturnInst>(Term) || isa<ResumeInst>(Term); | ||
}); | ||
if (doesNotReturn) | ||
newFunction->setDoesNotReturn(); | ||
|
||
LLVM_DEBUG(if (verifyFunction(*newFunction, &errs())) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't see why this does this, the regular post-pass verification would catch this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tested a simple case w.o this (using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NB the comment around when
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't talking about anything the pass does, I mean the verifyFunction call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, not sure what the need for that is. |
||
newFunction->dump(); | ||
report_fatal_error("verification of newFunction failed!"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be safer to do all_of unreachableinst. This requires maintaining a list of all possible returning terminators (though I guess this was just moved)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think that should be a follow up patch.
FWIW I tested this w/ hotcoldsplitting enabled (+ split all no-returns) on llvm-test-suite/spec/bootstrap and saw no issues.