mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
156 lines
4.8 KiB
C++
156 lines
4.8 KiB
C++
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/Passes/PassBuilder.h"
|
|
|
|
#include "revng/Model/FunctionTags.h"
|
|
#include "revng/Support/Debug.h"
|
|
#include "revng/Support/IRHelpers.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static bool isLastBeforeTerminator(Instruction *I) {
|
|
auto It = I->getIterator();
|
|
++It;
|
|
auto End = I->getParent()->end();
|
|
return It != End && ++It == End;
|
|
}
|
|
|
|
/// This pass hoists calls turns phis of calls returning a `StructType` into
|
|
/// a single call (in place of the original phi) whose arguments are phis of the
|
|
/// arguments of the original call.
|
|
/// This is currently necessary since the backend does not handle well
|
|
// `StructType`.
|
|
///
|
|
/// It turns:
|
|
///
|
|
/// a:
|
|
/// %x1 = call x(1, 2)
|
|
/// br c
|
|
/// b:
|
|
/// %x2 = call x(3, 4)
|
|
/// br c
|
|
/// c:
|
|
/// %x3 = phi [(a, %x1), (b, %x2)]
|
|
///
|
|
/// Into:
|
|
/// c:
|
|
/// %arg1 = phi [(a, 1), (b, 3)]
|
|
/// %arg2 = phi [(a, 2), (b, 4)]
|
|
/// %x3 = call x(%arg1, %arg2)
|
|
///
|
|
/// \note This can be done only when x does not have side-effects.
|
|
class HoistStructPhis : public llvm::FunctionPass {
|
|
public:
|
|
static char ID;
|
|
HoistStructPhis() : llvm::FunctionPass(ID) {}
|
|
|
|
bool runOnFunction(llvm::Function &F) override {
|
|
llvm::SmallVector<PHINode *, 16> ToFix;
|
|
|
|
// Collect phis that need fixing
|
|
for (BasicBlock &BB : F)
|
|
for (Instruction &I : BB)
|
|
if (auto *Phi = dyn_cast<PHINode>(&I))
|
|
if (isa<StructType>(I.getType()))
|
|
ToFix.push_back(Phi);
|
|
|
|
if (ToFix.size() == 0)
|
|
return false;
|
|
|
|
for (PHINode *Phi : ToFix)
|
|
handlePhi(Phi);
|
|
|
|
return true;
|
|
}
|
|
|
|
void handlePhi(PHINode *Phi) {
|
|
auto PhiSize = Phi->getNumIncomingValues();
|
|
|
|
if (PhiSize == 1) {
|
|
Phi->replaceAllUsesWith(Phi->getIncomingValue(0));
|
|
return;
|
|
}
|
|
|
|
// Create all the phis
|
|
CallInst *FirstCall = nullptr;
|
|
Value *CalledValue = nullptr;
|
|
llvm::SmallVector<Value *, 2> Phis;
|
|
llvm::SmallVector<CallInst *, 2> Calls;
|
|
|
|
for (auto &V : Phi->incoming_values()) {
|
|
if (auto *Call = dyn_cast<CallInst>(V.get())) {
|
|
if (CalledValue == nullptr) {
|
|
// This is the first call we see, make some extra checks
|
|
CalledValue = Call->getCalledOperand();
|
|
FirstCall = Call;
|
|
|
|
Function *Callee = getCalledFunction(Call);
|
|
|
|
// Ignore isolated functions, they are not side-effect free
|
|
if (Callee == nullptr or FunctionTags::Isolated.isTagOf(Callee))
|
|
return;
|
|
|
|
revng_assert(isLastBeforeTerminator(Call)
|
|
or Callee->onlyReadsMemory());
|
|
|
|
// First iteration, create phis
|
|
for (Type *ArgumentType : Call->getFunctionType()->params()) {
|
|
llvm::Instruction *NewPhi = PHINode::Create(ArgumentType,
|
|
PhiSize,
|
|
"",
|
|
Phi);
|
|
NewPhi->setDebugLoc(Phi->getDebugLoc());
|
|
Phis.push_back(NewPhi);
|
|
}
|
|
|
|
Calls.push_back(Call);
|
|
|
|
} else {
|
|
// Ensure all the incomings are calls to the same function
|
|
revng_assert(CalledValue == Call->getCalledOperand());
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto &&[V, Predecessor] : zip(Phi->incoming_values(), Phi->blocks())) {
|
|
if (isa<UndefValue>(V)) {
|
|
for (auto *NewPhi : Phis)
|
|
cast<PHINode>(NewPhi)->addIncoming(UndefValue::get(NewPhi->getType()),
|
|
Predecessor);
|
|
} else if (isa<PoisonValue>(V)) {
|
|
for (auto *NewPhi : Phis) {
|
|
auto *Poison = PoisonValue::get(NewPhi->getType());
|
|
cast<PHINode>(NewPhi)->addIncoming(Poison, Predecessor);
|
|
}
|
|
} else if (auto *Call = dyn_cast<CallInst>(V)) {
|
|
revng_assert(Call->arg_size() == Phis.size());
|
|
for (auto &&[Argument, NewPhi] : zip(Call->args(), Phis))
|
|
cast<PHINode>(NewPhi)->addIncoming(Argument, Predecessor);
|
|
}
|
|
}
|
|
|
|
// Create a new function call
|
|
Instruction *InsertionPoint = Phi->getParent()->getFirstNonPHI();
|
|
auto *NewCall = CallInst::Create({ FirstCall->getFunctionType(),
|
|
CalledValue },
|
|
ArrayRef<Value *>(Phis),
|
|
ArrayRef<OperandBundleDef>{},
|
|
"",
|
|
InsertionPoint);
|
|
|
|
// Steal metadata and replace original phi
|
|
NewCall->copyMetadata(*FirstCall);
|
|
revng_assert(NewCall->getType() == Phi->getType());
|
|
Phi->replaceAllUsesWith(NewCall);
|
|
Phi->eraseFromParent();
|
|
|
|
for (CallInst *Call : Calls)
|
|
eraseFromParent(Call);
|
|
}
|
|
};
|
|
|
|
char HoistStructPhis::ID;
|
|
static RegisterPass<HoistStructPhis> R("hoist-struct-phis", "", false, false);
|