[FuzzMutate] New InsertPHINode strategy.

PHI Node can't be modeled like other instructions since its operand
number depends on predecessors. So we have a stand alone strategy for it.

Signed-off-by: Peter Rong <PeterRong96@gmail.com>

Reviewed By: arsenm

Differential Revision: https://reviews.llvm.org/D138959
This commit is contained in:
Peter Rong 2022-11-28 18:42:11 -08:00
parent 0926035ed5
commit 4be0873471
5 changed files with 114 additions and 0 deletions

View File

@ -118,6 +118,17 @@ public:
void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
};
/// Strategy to insert PHI Nodes at the head of each basic block.
class InsertPHIStrategy : public IRMutationStrategy {
public:
uint64_t getWeight(size_t CurrentSize, size_t MaxSize,
uint64_t CurrentWeight) override {
return 2;
}
void mutate(BasicBlock &BB, RandomIRBuilder &IB) override;
};
/// Strategy to select a random instruction and add a new sink (user) to it to
/// increate data dependency.
class SinkInstructionStrategy : public IRMutationStrategy {

View File

@ -61,6 +61,8 @@ struct RandomIRBuilder {
ArrayRef<Value *> Srcs, fuzzerop::SourcePred Pred);
Type *chooseType(LLVMContext &Context, ArrayRef<Value *> Srcs,
fuzzerop::SourcePred Pred);
/// Return a uniformly choosen type from \c AllowedTypes
Type *randomType();
};
} // namespace llvm

View File

@ -299,6 +299,35 @@ void InstModificationIRStrategy::mutate(Instruction &Inst,
RS.getSelection()();
}
void InsertPHIStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
// Can't insert PHI node to entry node.
if (&BB == &BB.getParent()->getEntryBlock())
return;
Type *Ty = IB.randomType();
PHINode *PHI = PHINode::Create(Ty, llvm::pred_size(&BB), "", &BB.front());
// Use a map to make sure the same incoming basic block has the same value.
DenseMap<BasicBlock *, Value *> IncomingValues;
for (BasicBlock *Pred : predecessors(&BB)) {
Value *Src = IncomingValues[Pred];
// If `Pred` is not in the map yet, we'll get a nullptr.
if (!Src) {
SmallVector<Instruction *, 32> Insts;
for (auto I = Pred->begin(); I != Pred->end(); ++I)
Insts.push_back(&*I);
// There is no need to inform IB what previously used values are if we are
// using `onlyType`
Src = IB.findOrCreateSource(*Pred, Insts, {}, fuzzerop::onlyType(Ty));
IncomingValues[Pred] = Src;
}
PHI->addIncoming(Src, Pred);
}
SmallVector<Instruction *, 32> InstsAfter;
for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I)
InstsAfter.push_back(&*I);
IB.connectToSink(BB, InstsAfter, PHI);
}
void SinkInstructionStrategy::mutate(Function &F, RandomIRBuilder &IB) {
for (BasicBlock &BB : F) {
this->mutate(BB, IB);

View File

@ -169,3 +169,8 @@ Value *RandomIRBuilder::findPointer(BasicBlock &BB,
return RS.getSelection();
return nullptr;
}
Type *RandomIRBuilder::randomType() {
uint64_t TyIdx = uniform<uint64_t>(Rand, 0, KnownTypes.size() - 1);
return KnownTypes[TyIdx];
}

View File

@ -12,6 +12,7 @@
#include "llvm/AsmParser/SlotMapping.h"
#include "llvm/FuzzMutate/IRMutator.h"
#include "llvm/FuzzMutate/Operations.h"
#include "llvm/FuzzMutate/RandomIRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
@ -310,6 +311,72 @@ TEST(InstModificationIRStrategyTest, DidntShuffleFRem) {
VerfyDivDidntShuffle(Source);
}
TEST(InsertPHIStrategy, PHI) {
LLVMContext Ctx;
StringRef Source = "\n\
define void @test(i1 %C1, i1 %C2, i32 %I, double %FP) { \n\
Entry: \n\
%C = and i1 %C1, %C2 \n\
br i1 %C, label %LoopHead, label %Exit \n\
LoopHead: ; pred Entry, LoopBody \n\
switch i32 %I, label %Default [ \n\
i32 1, label %OnOne \n\
i32 2, label %OnTwo \n\
i32 3, label %OnThree \n\
] \n\
Default: \n\
br label %LoopBody \n\
OnOne: ; pred LoopHead \n\
%DFP = fmul double %FP, 2.0 \n\
%OnOneCond = fcmp ogt double %DFP, %FP \n\
br i1 %OnOneCond, label %LoopBody, label %Exit \n\
OnTwo: ; pred Entry \n\
br i1 %C1, label %OnThree, label %LoopBody \n\
OnThree: ; pred Entry, OnTwo, OnThree \n\
br i1 %C2, label %OnThree, label %LoopBody \n\
LoopBody: ; pred Default, OnOne, OnTwo, OnThree \n\
br label %LoopHead \n\
Exit: ; pred Entry, OnOne \n\
ret void \n\
}";
auto Mutator = createMutator<InsertPHIStrategy>();
ASSERT_TRUE(Mutator);
auto M = parseAssembly(Source.data(), Ctx);
std::mt19937 mt(Seed);
std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX);
for (int i = 0; i < 100; i++) {
Mutator->mutateModule(*M, RandInt(mt), Source.size(), Source.size() + 1024);
ASSERT_FALSE(verifyModule(*M, &errs()));
}
}
TEST(InsertPHIStrategy, PHIWithSameIncomingBlock) {
LLVMContext Ctx;
StringRef Source = "\n\
define void @test(i32 %I) { \n\
Entry: \n\
switch i32 %I, label %Exit [ \n\
i32 1, label %IdentCase \n\
i32 2, label %IdentCase \n\
i32 3, label %IdentCase \n\
i32 4, label %IdentCase \n\
] \n\
IdentCase: \n\
br label %Exit \n\
Exit: \n\
ret void \n\
}";
auto IPS = std::make_unique<InsertPHIStrategy>();
RandomIRBuilder IB(Seed, {IntegerType::getInt32Ty(Ctx)});
auto M = parseAssembly(Source.data(), Ctx);
Function &F = *M->begin();
for (auto &BB : F) {
IPS->mutate(BB, IB);
ASSERT_FALSE(verifyModule(*M, &errs()));
}
}
TEST(SinkInstructionStrategy, Operand) {
LLVMContext Ctx;
StringRef Source = "\n\