Skip to content

Commit

Permalink
[FuzzMutate] New InsertPHINode strategy.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
DataCorrupted committed Nov 30, 2022
1 parent 0926035 commit 4be0873
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 0 deletions.
11 changes: 11 additions & 0 deletions llvm/include/llvm/FuzzMutate/IRMutator.h
Expand Up @@ -118,6 +118,17 @@ class InstModificationIRStrategy : public IRMutationStrategy {
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 {
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/FuzzMutate/RandomIRBuilder.h
Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions llvm/lib/FuzzMutate/IRMutator.cpp
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/FuzzMutate/RandomIRBuilder.cpp
Expand Up @@ -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];
}
67 changes: 67 additions & 0 deletions llvm/unittests/FuzzMutate/StrategiesTest.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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\
Expand Down

0 comments on commit 4be0873

Please sign in to comment.