FuzzMutate didn't consider some corner cases and leads to mutation failure when mutating some modules.
This patch fixes 3 bugs:
- Add null check when encountering basic blocks without predecessor to avoid segmentation fault
- Avoid insertion after `musttail call` instruction
- Avoid sinking token type
Unit tests are also added.
Reviewed By: Peter
Differential Revision: https://reviews.llvm.org/D151936
return *RS;
}
+static inline iterator_range<BasicBlock::iterator>
+getInsertionRange(BasicBlock &BB) {
+ auto End = BB.getTerminatingMustTailCall() ? std::prev(BB.end()) : BB.end();
+ return make_range(BB.getFirstInsertionPt(), End);
+}
+
void InjectorIRStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
SmallVector<Instruction *, 32> Insts;
- for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I)
- Insts.push_back(&*I);
+ for (Instruction &I : getInsertionRange(BB))
+ Insts.push_back(&I);
if (Insts.size() < 1)
return;
auto RS = makeSampler(IB.Rand, Functions);
Function *F = RS.getSelection();
+ // Some functions accept metadata type or token type as arguments.
+ // We don't call those functions for now.
+ // For example, `@llvm.dbg.declare(metadata, metadata, metadata)`
+ // https://llvm.org/docs/SourceLevelDebugging.html#llvm-dbg-declare
auto IsUnsupportedTy = [](Type *T) {
return T->isMetadataTy() || T->isTokenTy();
};
};
SmallVector<Instruction *, 32> Insts;
- for (Instruction &I : make_range(BB.getFirstInsertionPt(), BB.end()))
+ for (Instruction &I : getInsertionRange(BB))
Insts.push_back(&I);
if (Insts.size() < 1)
return;
void InsertCFGStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
SmallVector<Instruction *, 32> Insts;
- for (Instruction &I : make_range(BB.getFirstInsertionPt(), BB.end()))
+ for (Instruction &I : getInsertionRange(BB))
Insts.push_back(&I);
if (Insts.size() < 1)
return;
PHI->addIncoming(Src, Pred);
}
SmallVector<Instruction *, 32> InstsAfter;
- for (Instruction &I : make_range(BB.getFirstInsertionPt(), BB.end()))
+ for (Instruction &I : getInsertionRange(BB))
InstsAfter.push_back(&I);
IB.connectToSink(BB, InstsAfter, PHI);
}
}
void SinkInstructionStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
SmallVector<Instruction *, 32> Insts;
- for (Instruction &I : make_range(BB.getFirstInsertionPt(), BB.end()))
+ for (Instruction &I : getInsertionRange(BB))
Insts.push_back(&I);
if (Insts.size() < 1)
return;
Instruction *Inst = Insts[Idx];
// `Idx + 1` so we don't sink to ourselves.
auto InstsAfter = ArrayRef(Insts).slice(Idx + 1);
- LLVMContext &C = BB.getParent()->getParent()->getContext();
- // Don't sink terminators, void function calls, etc.
- if (Inst->getType() != Type::getVoidTy(C))
+ Type *Ty = Inst->getType();
+ // Don't sink terminators, void function calls, token, etc.
+ if (!Ty->isVoidTy() && !Ty->isTokenTy())
// Find a new sink and wire up the results of the operation.
IB.connectToSink(BB, InstsAfter, Inst);
}
static std::vector<BasicBlock *> getDominators(BasicBlock *BB) {
std::vector<BasicBlock *> ret;
DominatorTree DT(*BB->getParent());
- DomTreeNode *Node = DT[BB]->getIDom();
+ DomTreeNode *Node = DT.getNode(BB);
+ // It's possible that an orphan block is not in the dom tree. In that case we
+ // just return nothing.
+ if (!Node)
+ return ret;
+ Node = Node->getIDom();
while (Node && Node->getBlock()) {
ret.push_back(Node->getBlock());
// Get parent block.
static std::vector<BasicBlock *> getDominatees(BasicBlock *BB) {
DominatorTree DT(*BB->getParent());
std::vector<BasicBlock *> ret;
- for (DomTreeNode *Child : DT[BB]->children())
+ DomTreeNode *Parent = DT.getNode(BB);
+ // It's possible that an orphan block is not in the dom tree. In that case we
+ // just return nothing.
+ if (!Parent)
+ return ret;
+ for (DomTreeNode *Child : Parent->children())
ret.push_back(Child->getBlock());
uint64_t Idx = 0;
while (Idx < ret.size()) {
}
ASSERT_FALSE(Modified);
}
+
+TEST(RandomIRBuilderTest, SrcAndSinkWOrphanBlock) {
+ const char *Source = "\n\
+ define i1 @test(i1 %Bool, i32 %Int, i64 %Long) { \n\
+ Entry: \n\
+ %Eq0 = icmp eq i64 %Long, 0 \n\
+ br i1 %Eq0, label %True, label %False \n\
+ True: \n\
+ %Or = or i1 %Bool, %Eq0 \n\
+ ret i1 %Or \n\
+ False: \n\
+ %And = and i1 %Bool, %Eq0 \n\
+ ret i1 %And \n\
+ Orphan_1: \n\
+ %NotBool = sub i1 1, %Bool \n\
+ ret i1 %NotBool \n\
+ Orphan_2: \n\
+ %Le42 = icmp sle i32 %Int, 42 \n\
+ ret i1 %Le42 \n\
+ }";
+ LLVMContext Ctx;
+ std::mt19937 mt(Seed);
+ std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX);
+ std::array<Type *, 3> IntTys(
+ {Type::getInt64Ty(Ctx), Type::getInt32Ty(Ctx), Type::getInt1Ty(Ctx)});
+ std::vector<Value *> Constants;
+ for (Type *IntTy : IntTys) {
+ for (size_t v : {1, 42}) {
+ Constants.push_back(ConstantInt::get(IntTy, v));
+ }
+ }
+ for (int i = 0; i < 10; i++) {
+ RandomIRBuilder IB(RandInt(mt), IntTys);
+ std::unique_ptr<Module> M = parseAssembly(Source, Ctx);
+ Function &F = *M->getFunction("test");
+ for (BasicBlock &BB : F) {
+ SmallVector<Instruction *, 4> Insts;
+ for (Instruction &I : BB) {
+ Insts.push_back(&I);
+ }
+ for (int j = 0; j < 10; j++) {
+ IB.findOrCreateSource(BB, Insts);
+ }
+ for (Value *V : Constants) {
+ IB.connectToSink(BB, Insts, V);
+ }
+ }
+ }
+}
} // namespace
mutateAndVerifyModule(Source, Mutator, 100);
}
+TEST(InjectorIRStrategyTest, InsertWMustTailCall) {
+ StringRef Source = "\n\
+ define i1 @recursive() { \n\
+ Entry: \n\
+ %Ret = musttail call i1 @recursive() \n\
+ ret i1 %Ret \n\
+ }";
+ auto Mutator = createInjectorMutator();
+ ASSERT_TRUE(Mutator);
+ mutateAndVerifyModule(Source, Mutator, 100);
+}
+
+TEST(InjectorIRStrategyTest, InsertWTailCall) {
+ StringRef Source = "\n\
+ define i1 @recursive() { \n\
+ Entry: \n\
+ %Ret = tail call i1 @recursive() \n\
+ ret i1 %Ret \n\
+ }";
+ auto Mutator = createInjectorMutator();
+ ASSERT_TRUE(Mutator);
+ mutateAndVerifyModule(Source, Mutator, 100);
+}
+
TEST(InstDeleterIRStrategyTest, EmptyFunction) {
// Test that we don't crash even if we can't remove from one of the functions.
mutateAndVerifyModule<SinkInstructionStrategy>(Source);
}
+TEST(SinkInstructionStrategy, DoNotSinkTokenType) {
+ StringRef Source = "\n\
+ declare ptr @fake_personality_function() \n\
+ declare token @llvm.experimental.gc.statepoint.p0(i64 immarg %0, i32 immarg %1, ptr %2, i32 immarg %3, i32 immarg %4, ...) \n\
+ define void @test() gc \"statepoint-example\" personality ptr @fake_personality_function { \n\
+ Entry: \n\
+ %token1 = call token (i64, i32, ptr, i32, i32, ...) \
+ @llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(ptr addrspace(1) ()) undef, i32 0, i32 0, i32 0, i32 0) \n\
+ ret void \n\
+ }";
+ mutateAndVerifyModule<SinkInstructionStrategy>(Source);
+}
+
static void VerifyBlockShuffle(StringRef Source) {
LLVMContext Ctx;
auto Mutator = createMutator<ShuffleBlockStrategy>();