#include "ReduceOperandsToArgs.h"
#include "Delta.h"
#include "Utils.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
using namespace llvm;
static bool canReplaceFunction(Function *F) {
return all_of(F->uses(), [](Use &Op) {
if (auto *CI = dyn_cast<CallBase>(Op.getUser()))
return &CI->getCalledOperandUse() == &Op;
return false;
});
}
static bool canReduceUse(Use &Op) {
Value *Val = Op.get();
Type *Ty = Val->getType();
if (!Ty->isFirstClassType())
return false;
if (Ty->isLabelTy() || Ty->isMetadataTy())
return false;
if (isa<Argument>(Val))
return false;
if (isa<ConstantData>(Val))
return false;
if (auto *CI = dyn_cast<CallBase>(Op.getUser()))
if (&CI->getCalledOperandUse() == &Op)
return false;
return true;
}
static void replaceFunctionCalls(Function *OldF, Function *NewF) {
SmallVector<CallBase *> Callers;
for (Use &U : OldF->uses()) {
auto *CI = cast<CallBase>(U.getUser());
assert(&U == &CI->getCalledOperandUse());
assert(CI->getCalledFunction() == OldF);
Callers.push_back(CI);
}
SmallVector<Value *> Args(NewF->arg_size(), nullptr);
for (auto ArgIdx : llvm::seq<size_t>(OldF->arg_size(), NewF->arg_size())) {
Type *NewArgTy = NewF->getArg(ArgIdx)->getType();
Args[ArgIdx] = getDefaultValue(NewArgTy);
}
for (CallBase *CI : Callers) {
for (auto Z : zip_first(CI->args(), Args))
std::get<1>(Z) = std::get<0>(Z);
SmallVector<OperandBundleDef> OperandBundles;
CI->getOperandBundlesAsDefs(OperandBundles);
CallBase *NewCI;
if (auto *II = dyn_cast<InvokeInst>(CI)) {
NewCI = InvokeInst::Create(NewF, cast<InvokeInst>(II)->getNormalDest(),
cast<InvokeInst>(II)->getUnwindDest(), Args,
OperandBundles, CI->getName());
} else {
assert(isa<CallInst>(CI));
NewCI = CallInst::Create(NewF, Args, OperandBundles, CI->getName());
}
NewCI->setCallingConv(NewF->getCallingConv());
if (!CI->use_empty())
CI->replaceAllUsesWith(NewCI);
ReplaceInstWithInst(CI, NewCI);
}
}
static void substituteOperandWithArgument(Function *OldF,
ArrayRef<Use *> OpsToReplace) {
if (OpsToReplace.empty())
return;
SetVector<Value *> UniqueValues;
for (Use *Op : OpsToReplace)
UniqueValues.insert(Op->get());
SmallVector<Type *> NewArgTypes;
llvm::append_range(NewArgTypes, OldF->getFunctionType()->params());
size_t ArgOffset = NewArgTypes.size();
for (Value *V : UniqueValues)
NewArgTypes.push_back(V->getType());
FunctionType *FTy =
FunctionType::get(OldF->getFunctionType()->getReturnType(), NewArgTypes,
OldF->getFunctionType()->isVarArg());
Function *NewF =
Function::Create(FTy, OldF->getLinkage(), OldF->getAddressSpace(),
OldF->getName(), OldF->getParent());
NewF->removeFromParent();
OldF->getParent()->getFunctionList().insertAfter(OldF->getIterator(), NewF);
ValueToValueMapTy VMap;
for (auto Z : zip_first(OldF->args(), NewF->args())) {
Argument &OldArg = std::get<0>(Z);
Argument &NewArg = std::get<1>(Z);
NewArg.setName(OldArg.getName()); VMap[&OldArg] = &NewArg; }
ValueToValueMapTy OldValMap;
for (auto Z : zip_first(UniqueValues, drop_begin(NewF->args(), ArgOffset))) {
Value *OldVal = std::get<0>(Z);
Argument &NewArg = std::get<1>(Z);
NewArg.setName(OldVal->getName());
OldValMap[OldVal] = &NewArg;
}
SmallVector<ReturnInst *, 8> Returns; CloneFunctionInto(NewF, OldF, VMap, CloneFunctionChangeType::LocalChangesOnly,
Returns, "", nullptr);
for (Use *Op : OpsToReplace) {
Value *NewArg = OldValMap.lookup(Op->get());
auto *NewUser = cast<Instruction>(VMap.lookup(Op->getUser()));
NewUser->setOperand(Op->getOperandNo(), NewArg);
}
replaceFunctionCalls(OldF, NewF);
std::string FName = OldF->getName().str();
OldF->replaceAllUsesWith(ConstantExpr::getBitCast(NewF, OldF->getType()));
OldF->eraseFromParent();
NewF->setName(FName);
}
static void reduceOperandsToArgs(Oracle &O, Module &Program) {
SmallVector<Use *> OperandsToReduce;
for (Function &F : make_early_inc_range(Program.functions())) {
if (!canReplaceFunction(&F))
continue;
OperandsToReduce.clear();
for (Instruction &I : instructions(&F)) {
for (Use &Op : I.operands()) {
if (!canReduceUse(Op))
continue;
if (O.shouldKeep())
continue;
OperandsToReduce.push_back(&Op);
}
}
substituteOperandWithArgument(&F, OperandsToReduce);
}
}
void llvm::reduceOperandsToArgsDeltaPass(TestRunner &Test) {
outs() << "*** Converting operands to function arguments ...\n";
return runDeltaPass(Test, reduceOperandsToArgs);
}