[IR] Don't use blockaddresses as callbr arguments

Following some recent discussions, this changes the representation
of callbrs in IR. The current blockaddress arguments are replaced
with `!` label constraints that refer directly to callbr indirect
destinations:

    ; Before:
    %res = callbr i8* asm "", "=r,r,i"(i8* %x, i8* blockaddress(@test8, %foo))
    to label %asm.fallthrough [label %foo]
    ; After:
    %res = callbr i8* asm "", "=r,r,!i"(i8* %x)
    to label %asm.fallthrough [label %foo]

The benefit of this is that we can easily update the successors of
a callbr, without having to worry about also updating blockaddress
references. This should allow us to remove some limitations:

* Allow unrolling/peeling/rotation of callbr, or any other
  clone-based optimizations
  (https://github.com/llvm/llvm-project/issues/41834)
* Allow duplicate successors
  (https://github.com/llvm/llvm-project/issues/45248)

This is just the IR representation change though, I will follow up
with patches to remove limtations in various transformation passes
that are no longer needed.

Differential Revision: https://reviews.llvm.org/D129288
This commit is contained in:
Nikita Popov
2022-07-07 12:27:43 +02:00
parent cae5462a2c
commit 2a721374ae
74 changed files with 384 additions and 290 deletions

View File

@@ -5510,6 +5510,61 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
if (!OperandBundles.empty())
UpgradeOperandBundles(OperandBundles);
if (auto *IA = dyn_cast<InlineAsm>(Callee)) {
InlineAsm::ConstraintInfoVector ConstraintInfo = IA->ParseConstraints();
auto IsLabelConstraint = [](const InlineAsm::ConstraintInfo &CI) {
return CI.Type == InlineAsm::isLabel;
};
if (none_of(ConstraintInfo, IsLabelConstraint)) {
// Upgrade explicit blockaddress arguments to label constraints.
// Verify that the last arguments are blockaddress arguments that
// match the indirect destinations. Clang always generates callbr
// in this form. We could support reordering with more effort.
unsigned FirstBlockArg = Args.size() - IndirectDests.size();
for (unsigned ArgNo = FirstBlockArg; ArgNo < Args.size(); ++ArgNo) {
unsigned LabelNo = ArgNo - FirstBlockArg;
auto *BA = dyn_cast<BlockAddress>(Args[ArgNo]);
if (!BA || BA->getFunction() != F ||
LabelNo > IndirectDests.size() ||
BA->getBasicBlock() != IndirectDests[LabelNo])
return error("callbr argument does not match indirect dest");
}
// Remove blockaddress arguments.
Args.erase(Args.begin() + FirstBlockArg, Args.end());
ArgTyIDs.erase(ArgTyIDs.begin() + FirstBlockArg, ArgTyIDs.end());
// Recreate the function type with less arguments.
SmallVector<Type *> ArgTys;
for (Value *Arg : Args)
ArgTys.push_back(Arg->getType());
FTy =
FunctionType::get(FTy->getReturnType(), ArgTys, FTy->isVarArg());
// Update constraint string to use label constraints.
std::string Constraints = IA->getConstraintString();
unsigned ArgNo = 0;
size_t Pos = 0;
for (const auto &CI : ConstraintInfo) {
if (CI.hasArg()) {
if (ArgNo >= FirstBlockArg)
Constraints.insert(Pos, "!");
++ArgNo;
}
// Go to next constraint in string.
Pos = Constraints.find(',', Pos);
if (Pos == std::string::npos)
break;
++Pos;
}
Callee = InlineAsm::get(FTy, IA->getAsmString(), Constraints,
IA->hasSideEffects(), IA->isAlignStack(),
IA->getDialect(), IA->canThrow());
}
}
I = CallBrInst::Create(FTy, Callee, DefaultDest, IndirectDests, Args,
OperandBundles);
ResTypeID = getContainedTypeID(FTyID);